Yii PHP фреймворк: оформление административных страниц

Владимир | | HTML, PHP, Web разработка, Yii.

yii grid view

Одной из наиболее мощных возможностей фреймворка Yii является генерация кода. Она позволяет сразу после создания таблицы в базе данных получить файлы модели, контроллера и представлений. Т.е. весь необходимый код для выполнения CRUD операций.

Но, естественно, код формируется по стандартному шаблону, дизайн которого вам, возможно, захочется изменить.

Взгляните на обычную страницу управления записями, сформированную с помощью компонента CGridView.

basic grid thumb

Такая страница доступна по адресу
sitename.domen?r=controller/admin

Она состоит из трех частей.

1) Строки с номерами показанных записей и их количеством (summary).

2) Таблицы с данными (items).

3) Листалки (pager). Если записей в таблице меньше, чем отображается на странице, этот элемент показан не будет.

Теперь взгляните на код, который создает такую таблицу.

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'id'=>'cities-grid',
	'dataProvider'=>$model->search(),
	'filter'=>$model,
	'columns'=>array(
		'id',
		'city',
		'country',
		array(
			'class'=>'CButtonColumn',
		),
	),
)); ?>

Источником данных служит объект CActiveDataProvider, который создается в контроллере. Но сейчас речь не о данных.

Попробуем изменить порядок вывода основных элементов.

CGridView имеет параметр template, который позволяет управлять выводом основных элементов страницы. Этот параметр принимает строку, в которой нужно перечислить необходимые элементы страницы.

Например, следующий код покажет листалку сверху и снизу страницы.

<?php $this->widget('zii.widgets.grid.CGridView', array(
	…,
	'template'=>'{summary} {pager} {items} {pager}',
	'columns'=>array(
		…,
	),
)); ?>

В шаблон можно вставлять произвольный текст и теги, но это не очень удобный вариант изменения разметки страницы. Лучше менять разметку внутри самих элементов и/или использовать CSS стили.

Если нужно изменить дизайн, то я бы рекомендовал делать в следующей последовательности.

1) Попытаться добиться результата, не меняя разметку, с помощью CSS стилей. Разметка, которую создаёт фреймворк, достаточна простая и соответствует стандартам.

2) Если необходимо, изменить разметку.

Рассмотрим первый вариант – использование своих CSS стилей.

По-умолчанию, для оформления CGridView используются следующие CSS файлы:

1) Для оформления таблицы framework/zii/widgets/assets/gridview/styles.css (он автоматически копируется в папку DOCUMENT_ROOT/accets/номер/gridview/styles.css).

2) Для оформления листалки framework/web/widgets/pagers/pager.css (он копируется в assets/номер/pager.css).

Допустим, ваши стили находятся в файлах gridview-styles.css и pager-styles.css, которые расположены в папке DOCUMENT_ROOT/css.

Подключаем файл со стилями таблицы, для этого передаем ссылку на файл в параметре cssFile.

<?php $this->widget('zii.widgets.grid.CGridView', array(
	…,
	'cssFile'=>Yii::app()->getBaseUrl(true).'/css/admin-styles.css',
	'columns'=>array(
		…,
	),
)); ?>

В результате, фреймворк будет загружать ваш файл вместо стандартного.

Кроме того, есть несколько параметров (itemsCssClass, rowCssClass, summaryCssClass и др.), которые позволяют указать ваши имена CSS классов. Но, вы можете использовать и стандартные имена классов, например, взять стандартный файл со стилями и переделывать его под свой дизайн.

Установка стилей для листалки (pager) немного отличается.

Из CGridView нужно получить доступ к атрибуту pager и устанавливать параметры для него.

<?php $this->widget('zii.widgets.grid.CGridView', array(
	…,
	'pager'=>array('cssFile'=>Yii::app()->getBaseUrl(true).'/css/pager-styles.css'),	'columns'=>array(
		…,
	),
)); ?>

Код похожий, но параметр cssFile передается в массиве и присваивается атрибуту pager.

Тут возникает интересный вопрос: «Какие еще параметры мы можем передать в этом массиве?»

Любые атрибуты класса CBasePager, а также его потомков, в том числе и имя класса-потомка, который используется для создания листалки.

В дистрибутив входит два таких класса CLinkPager (создает список ссылок с номерами страниц, используется по-умолчанию) и CListPager (создает выпадающий список с номерами страниц).

Заменить CLinkPager на CListPager можно так.

…
	'pager'=>array('class'=>'CListPager'),
…

Тут мы подходим к вопросу изменения разметки.

Допустим, нам нужно создать листалку в которой элементы страниц будут находиться внутри тегов span, а не li, как выводит CLinkPager.

Можно, конечно, создать класс, производный от CBasePager, но в данном случае будет удобнее наследовать CLinkPager.

У меня получился такой код (файл protected/components /SpanLinkPager.php).

class SpanLinkPager extends CLinkPager {

	public function run()
	{
		$buttons=$this->createPageButtons();
		if(empty($buttons))
			return;
		$this->registerClientScript();
		echo $this->header;
		echo CHtml::tag('div',$this->htmlOptions,implode("\n",$buttons));
		echo $this->footer;
	}

	protected function createPageButton($label,$page,$class,$hidden,$selected)
	{
		if($hidden || $selected)
			$class.=' '.($hidden ? self::CSS_HIDDEN_PAGE : self::CSS_SELECTED_PAGE);
		return '<span class="'.$class.'">'.CHtml::link($label,$this->createPageUrl($page)).'</span>';
	}
}

Я просто нашел в CLinkPager методы, в который формируется разметка и переопределил их. Т.е. код остался тот же, просто ul заменил на div (строка 12), а li — на span (строка 20).

Используя такой же подход можно переопределить класс CGridView и изменить разметку необходимым вам образом. Вот список основных методов, которые формируют разметку CGridView:
renderItems
renderTableHeader
renderTableFooter
renderTableBody
renderEmptyText
renderFilter
renderTableRow
renderSummary
(находится в CBaseListView, который является предком CGridView)

На этом мы остановимся. Конечно, в этой статье рассмотрены далеко не все нюансы использования компонента CGridView. Например, он позволяет управлять выводом данных, а также использовать скины, но об этом как-нибудь в другой раз 🙂

Если у вас возникли вопросы или есть замечания, пишите, постараюсь ответить.

До встречи!

  • Betaboy

    Yii рулит! )

  • Agree with Landlord sit.Various of online games provided are dynamic sense and free indefinitely.We are looking forward to you to here share the experience of
    exciting game competition! Dior homme shoes

  • BuCeFaL

    +1 )

  • SpongeBolt

    Спасибо большое!

  • sergi

    Всегда было интересно, а как верстальщику объяснить что он должен переопределить класс для того что бы изменить разметку.

  • Очень просто — никак 🙂
    Верстальщик на основе макета (psd) делает html страницу с нужной разметкой и css стилями. Если эта разметка совпадает с разметкой, которую создает CGridView (или ее можно безболезненно изменить так чтобы она совпадала), то использует стандартный компонент.
    В противном случае — программист должен переопределить класс.
    Естественно, очень желательно предварительно показать верстальщику стандартную разметку. В большинстве случаев всё решается с помощью css.

    • Sentox

      То есть верстальщик должен быть на порядок выше по специальности чем при обычных шаблонах?

      • Нет, почему выше?
        В любых шаблонах есть вставки кода в которых верстальщик должен ориентироваться.

  • Игорь

    Подскажите, пожалуйста, а как можно изменить название заголовков? Вместо ID, COUNTRY № и Страна

  • Эти изменения нужно вносить в модели (models/Countries.php).
    Там есть метод
    public function attributeLabels()
    {
    return array(
    'id' => '#',
    'name' => 'Страна',
    );
    }
    Ключи элементов массива будут соответствовать названиям полей в таблице.

  • Игорь

    Спасибо за ваши ответы, помогло)

    • Александр

       Так же можно назначить текст заголовка при описании полей , например:
      $this->widget('zii.widgets.grid.CGridView', array(
          'id' => 'ваш-grid',
          ……
          'columns' => array(
              'name',
              array(
                  'name' => 'наименование атрибута',
                  'header' => 'Текст заголовка',
                  'value' => 'Значение $data (модель)',
                  'filter' =>  Фильтр
              ),
          ……..

  • Professor

    Только начал изучать Yii, как хорошо что наткнулся на ваш блог =)
    Интересует вопрос, а как изменить текст summary?

    • Professor

      И текст листалки тоже хотелось бы поменять.

      • Используйте описанный в этой статье метод (второй вариант, где рассказывается об изменении разметки). Только для summary вам нужен метод CBaseListView.renderSummary.
        Переопределите его и можете выводить все, что угодно.

        Но если нужно изменить только текст, можно не переопределять метод.
        Используйте свойство
        summaryText

        • Professor

          Спасибо большое, разобрался =)

  • Arbuscula

    А если я хочу добавить еще один блок окромя {summary} {items} {pager}, например, {export}. Я так понял мне нужно переопределить CGridView и описать метод renderExport ?
    И еще вопрос, когда я переопределю класс CGridView. class MyGridView extends CGridView{…} Как он найдет класс CGridView от которого я наследую? Ведь он вызывался путем zii.widget.grid.CGridView.

    • Arbuscula

      Я написал в файле MyGridView прям перед определением класса строку: Yii::import('zii.widgets.grid.CGridView'); Правильно ли это ? Или нужно в main.php это прописывать ?

      • Вроде все правильно.
        Шаблоны {summary}, {items} и т.п. создаются с помощью renderSection(), который в свою очередь вызывает renderимя_шаблона (возможно, export нужно написать с маленькой буквы, не проверял).

        У вас появляются какие-нибудь ошибки при запуске кода?

  • Елена Сапрыгина

    Подскажите как просмотреть ячейку из бд, не нажатием стандартной кнопки, а нажатием на саму строчку, id или name

    • Я не совсем понимаю проблему. Повторил ваш код на тестовой установке yii. Ссылки нормально создались.

          'columns'=>array(
              'id',
              array(
                  'label'=>'test',
                  'header'=>'Name',
                  'urlExpression' => 'array(«/tblUser/view», «id» => $data->id)',
                  'class'=>'CLinkColumn',
              ),
              'username',
              'password',
              'email',
              array(
                  'class'=>'CButtonColumn',
              ),
          ),

  • Bum

    Спасибо!

  • Иван

    Скажите, а можно ли для постраничной навигации включить сразу два вида отображения, ссылки и выпадающий список?

    • Сделать можно, но я думаю придется создать отдельный

      CLinkPager и передать ему нужные параметры. Т.к. CGridView берет данные для создания постраничной навигации из CActiveDataProvider, а он содержит только одно свойство pagination.

  • Сергей

    Подскажите пожалуйста, а можно как-то сделать чтобы CGridView выводил таблицы с данными без имен заголовков? Мне надо выводить только строки с данными.

    • Сергей

      вроде сам разобрался… указал пустые названия в свойстве columns.

      • Есть еще один вариант
        $('.ui-jqgrid-hdiv').hide();

        • Сергей

          Спасибо!

        • Сергей

          оказывается у CGridView есть публичное свойство hideHeader, со значением по умолчанию true. Если его установить в false то заголовок выводится не будет.

  • Саша Сережевич

    Все это оч хорошо, но сталкнулся с делемой когда нужно в pagination добавить input type=»text» чтобы перейти на номер страницы введеный в инпуте. Не понятно как определить ссылку, вместо cteatePageUrl

    • Если я правильно понял, вы уже добавили форму с текстовым полем для ввода номера страницы. Проблема в формировании ссылки.
      Тут два варианта.

      Если у вас не используются pretty url (номера страниц передаются в обычных GET параметрах), то достаточно указать адрес первой страницы в атрибуте action формы.

      Если pretty url используются, то ссылку нужно формировать с помощью JavaScript. Вешаете обработчик на событие onsubmit формы и в нём добавляете параметр с номером страницы к url. И после этого выполняете переход на новый url:
      window.location = newUrl;

      • Саша Сережевич

        а если это ajaxUpdate ?

        • а какая разница? используйте событие onkeypress и в обработчике проверяете код клавиши enter.
          if (event.keyCode == 13) { …

        • Саша Сережевич

          не понятно как собрать другие параметры, сортировки, фильтра ? думал через serialize() но они идут без формы

        • по атрибутам id или name.
          $('input[name=»input_name»]')