Содержание статьи:
- Зачем нужен CGridView
- Подготовка
- Базовый вывод списка записей
- Стилизация виджета CGridView
- Поиск в виджете CGridView
- Настройка и стилизация колонок виджета CGridView
- Вывод данных со связанных таблицы
- Выпадающий список для поиска записей в виджете CGridView
- Поиск по дате с помощью datepicker
- Кнопки управления записями
- Выбор количества записей на страницу
- Итого
- Вывод
- Дополнительно
Зачем нужен CGridView
CGridView отображает список элементов данных в виде таблицы.
Каждая строка таблицы - это данные одного элемента из источника данных (чаще всего таблицы), а столбец обычно представляет атрибут элемента (некоторые столбцы могут соответствовать сложному выражению атрибутов или могут быть статическим текстом).
CGridView поддерживает сортировку и разбиение на страницы элементов из коробки. Сортировка и разбиение на страницы может быть реализована в режиме AJAX или в обычном запросе. Преимущество использования CGridView в том, что при отключенном JavaScript в браузере пользователя, сортировка и разбиение на страницы автоматически превращаются в обычные запросы и виджет нормально продолжает работать.
Подготовка
Для реализации примера нам потребуется 2 таблицы. Первая таблица будет содержать список статей, назовем ее posts. SQL запрос на создание таблицы:
CREATE TABLE IF NOT EXISTS `posts` ( `id` int(11) NOT NULL AUTO_INCREMENT, `url` varchar(255) NOT NULL, `header` varchar(255) NOT NULL, `text` longtext NOT NULL, `visibility` tinyint(1) NOT NULL, `date` int(11) NOT NULL, `userId` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Вторая будет содержать информацию о авторе статьи, назовем ее user. SQL запрос на создание таблицы:
CREATE TABLE IF NOT EXISTS `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, `password` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
Связь между таблицами user и posts будет один ко многим, т.е. 1 пользователь может иметь много статей. Для этих таблиц мы сгенерируем модели с помощью gii.
Также нам потребуется контроллер /protected/controllers/PostsController.php
для вывода списка записей. И представление /themes/themeName/posts/list.php
для вывода виджета CGridView.
Для получения привлекательного вида виджета будем использовать стиль Bootstrap 3. Предполагаю что Вы его уже подключили или можете использовать свой стиль.
И так приступим.
Базовый вывод списка записей
Код контроллера (файл: /protected/controllers/PostsController.php
):
<?php
class PostsController extends Controller {
//...
/** * Список записей */ public function actionList() { $model = new Posts('search'); $this->render('list', array('model' => $model)); } }
Код представления (view) (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'columns' => array( 'id', 'header', 'visibility', ), ));
В итоге мы получим следующие:
На данном этапе мы имеем список записей сортировку по полям (для сортировки необходимо кликнуть по заголовку таблицы нужной колонки) и постраничную навигацию.
Стилизация виджета CGridView
Продолжим. Стилицируем виджет CGridView под стиль Bootstrap 3. Для этого приведем представление к следующему виду (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(),
'id' => 'posts-grid', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader', 'columns' => array( 'id', 'header', 'visibility', ), ));
Теперь немного подробнее.
Строка 6, идентификатор виджета
'id' => 'posts-grid',
Строка 7, класс обертки виджета CGridView:
'htmlOptions' => array('class' => 'table'),
Строка 8, класс для таблицы виджета CGridView:
'itemsCssClass' => 'table table-hover table-striped',
Строка 9, класс прелоадера ajax загрузки данных. На данном этапе это сортировка и постраничная навигация:
'loadingCssClass' => 'grid-preloader',
Все стили уже есть в Bootstrap 3, кроме grid-preloader
, CSS для этого класса:
.grid-preloader{ background: url(/themes/themeName/images/preloader.gif) no-repeat center 0; width: 100%; z-index: 9999; }
При желании можно оставить стандартный прелоадер виджета, для этого просто удалите строку содержащую класс прелоадера (строка: 9).
Также необходимо добавить CSS стиль для классов .desc
и .asc
которые добаляються при сортировки в заголовок таблицы:
.sort-link.desc{ background:url(/themes/themeName/images/down.gif) right center no-repeat; padding-right: 10px; } .sort-link.asc{ background:url(/themes/themeName/images/up.gif) right center no-repeat; padding-right: 10px; }
down.gif и up.gif можно заменить своими или взять их из фреймворка (/framework/zii/widgets/assets/gridview
).
Стилизация постраничного навигатора
Для полноты картины необходимо привести в порядок вид постраничной навигации, добавим следующий код в виджет:
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ),
Итого, код представления примет вид (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(),
'id' => 'posts-grid', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ),
'columns' => array( 'id', 'header', 'visibility', ), ));
Подробнее о настройки постраничного навигатора можно прочитать здесь: http://кодер.укр/записи/yii_framework_настройка_и_стилизация_виджета_clinkpager_часть_2
Количество записей виджета CGridView на страницу
Для того что бы изменить количество записей на одну страницу необходимо поправить код модели Posts
, метод search()
(файл: /protected/models/Posts.php
):
<?php
class Posts extends CActiveRecord { //... public function search() { //... return new CActiveDataProvider($this, array( 'pagination' => array( 'pageSize' => 5, ), 'criteria' => $criteria, )); } //... }
В итоге виджет будет выглядеть следующим образом:
Поиск в виджете CGridView
Для того что бы добавить поиск по полям виджет CGridView в действие actionList()
контроллера PostsController
необходимо добавить следующий код:
$request = Yii::app()->request->getParam('Posts'); $model->unsetAttributes(); if (isset($request)) { $model->attributes = $request; }
Итого код действия примет вид (файл: /protected/controllers/PostsController.php
):
<?php class PostsController extends Controller { //... /** * Список записей */ public function actionList() { $model = new Posts('search'); $request = Yii::app()->request->getParam('Posts'); $model->unsetAttributes(); // clear any default values if (isset($request)) { $model->attributes = $request; } $this->render('list', array('model' => $model)); } }
А в представление добавить код:
'filter' => $model,
Следовательно код представления примет вид (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'filter' => $model,
'id' => 'posts-grid', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ),
'columns' => array( 'id', 'header', 'visibility', ), ));
Для полей по которым необходимо производить поиск по части значения (т.е. не точное совпадение) необходимо в методе search()
при вызове compare
третьим параметром ставить значение true
. Например как в случае поиска по заголовку, а для поиска по id
нам необходимо точное совпадение, пример:
<?php class Posts extends CActiveRecord { //... public function search() { //... $criteria->compare('id', $this->id); $criteria->compare('header', $this->header, true); //... } //... }
После чего виджет будет выглядеть следующим образом:
Настройка и стилизация колонок виджета CGridView
Приведем в порядок вид колонок. Уменьшим размер для поля поиска по id
, увеличим поле поиска по заголовку, в колонке visibility
вместо 0 и 1 будем выводить "Не опубликовано" или "Опубликовано" в виде иконок. Для этого нам необходимо значение элемента columns виджета привести к следующему виду:
array( array( 'name' => 'id', 'filterHtmlOptions' => array('class' => 'user-filter-id'), ), array( 'name' => 'header', 'filterHtmlOptions' => array('class' => 'user-filter-header'), ), array( 'name' => 'visibility', 'header' => 'Пуб.', 'htmlOptions' => array('class' => 'user-filter-visibility'), 'filterHtmlOptions' => array('class' => 'user-filter-visibility'), 'value' => '($data->visibility == 1 ? "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-open\" title=\"Опубликовано\"></span>" : "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-close\" title=\"Не опубликовано\"></span>")', 'type' => 'html' ), ),
СSS стили:
.user-filter-id{ width: 30px; } .user-filter-id input{ width: 30px; } .user-filter-header input{ width: 100%; } .user-filter-visibility input{ width: 30px; }
В итоге код представления примет вид (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'filter' => $model,
'id' => 'posts-grid', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ), 'columns' => array( array( 'name' => 'id', 'filterHtmlOptions' => array('class' => 'user-filter-id'), ), array( 'name' => 'header', 'filterHtmlOptions' => array('class' => 'user-filter-header'), ), array( 'name' => 'visibility', 'header' => 'Пуб.', 'htmlOptions' => array('class' => 'user-filter-visibility', 'style' => 'width:30px;text-align:center'), 'filterHtmlOptions' => array('class' => 'user-filter-visibility'), 'value' => '($data->visibility == 1 ? "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-open\" title=\"Опубликовано\"></span>" : "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-close\" title=\"Не опубликовано\"></span>")', 'type' => 'html' ), ), ));
После чего виджет будет выглядеть так:
Вывод данных со связанных таблицы
Добавим в список статей информация о авторе статьи, для этого в модель Posts
добавим связь в метод relations()
(файл: /protected/models/Posts.php
):
<?php
class Posts extends CActiveRecord { //... public function relations() { return array( //... 'user' => array(self::BELONGS_TO, 'User', 'userId'), ); } //... }
И добавим колонку "автор" в виджет CGridView:
array( 'name' => 'userId', 'header' => 'Автор', 'value' => '$data["user"]["name"];', ),
После чего код представления примет вид:
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'filter' => $model,
'id' => 'posts-grid', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ),
'columns' => array( array( 'name' => 'id', 'filterHtmlOptions' => array('class' => 'user-filter-id'), ), array( 'name' => 'header', 'filterHtmlOptions' => array('class' => 'user-filter-header'), ), array( 'name' => 'visibility', 'header' => 'Пуб.', 'filter' => array('' => 'Все', 1 => 'Опубликовано', 0 => 'Не опубликовано'), 'htmlOptions' => array('class' => 'user-filter-visibility', 'style' => 'width:30px;text-align:center'), 'filterHtmlOptions' => array('class' => 'user-filter-visibility'), 'value' => '($data->visibility == 1 ? "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-open\" title=\"Опубликовано\"></span>" : "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-close\" title=\"Не опубликовано\"></span>")', 'type' => 'html' ), array( 'name' => 'userId', 'header' => 'Автор', 'value' => '$data["user"]["name"];', ), ), ));
После чего виджет будет выглядеть так:
Выпадающий список для поиска записей в виджете CGridView
Для поиска по записям в виджете CGridView можно использовать выпадающий список (select). Приведем два простых примера поиска с использованием выпадающего списка
Простой выпадающий список
Для примера простого выпадающего списка будем использовать колонку visibility
и вместо обычного поля ввода дадим возможность выбора из трех вариантов: Все, Опубликовано, Не опубликовано. Для этого нам необходимо поправить колонку visibility
и добавить в нее следующий код (например, после элемента header
):
'filter' => array('' => 'Все', 1 => 'Опубликовано', 0 => 'Не опубликовано'),
С помощью CSS уменьшим длину select'a:
.user-filter-visibility select{ width: 40px; }
Выпадающий список из связанной таблицы
Сделаем поиск с помощью выпадающего списка по авторам статьи, для этого необходимо в колонку userId
добавить следующий код:
'filter' => array('' => 'Все') + CHtml::listData(User::model()->findAll(), 'id', 'name'),
Код представления
После добавления выпадающих списков в фильтр виджета CGridView, представление примет вид (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'filter' => $model,
'id' => 'posts-grid', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ),
'columns' => array( array( 'name' => 'id', 'filterHtmlOptions' => array('class' => 'user-filter-id'), ), array( 'name' => 'header', 'filterHtmlOptions' => array('class' => 'user-filter-header'), ), array( 'name' => 'visibility', 'header' => 'Пуб.', 'filter' => array('' => 'Все', 1 => 'Опубликовано', 0 => 'Не опубликовано'), 'htmlOptions' => array('class' => 'user-filter-visibility', 'style' => 'width:30px;text-align:center'), 'filterHtmlOptions' => array('class' => 'user-filter-visibility'), 'value' => '($data->visibility == 1 ? "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-open\" title=\"Опубликовано\"></span>" : "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-close\" title=\"Не опубликовано\"></span>")', 'type' => 'html' ), array( 'name' => 'userId', 'header' => 'Автор', 'filter' => array('' => 'Все') + CHtml::listData(User::model()->findAll(), 'id', 'name'), 'value' => '$data["user"]["name"];', ), ), ));
Виджет будет выглядеть так:
Поиск по дате с помощью datepicker
Для того что бы добавить поиск по дате, сначала необходимо добавить колонку с датой и в элементе filter
подключить datepicker
:
array( 'name' => 'date', 'type' => 'raw', 'filter' => $this->widget('zii.widgets.jui.CJuiDatePicker', array( 'model' => $model, 'attribute' => 'date', 'language' => 'ru', 'options' => array( 'showAnim' => 'fold', 'dateFormat' => 'dd.mm.yy', 'changeMonth' => 'true', 'changeYear' => 'true', 'showButtonPanel' => 'true', ), ), true), 'filterHtmlOptions' => array('class' => 'user-filter-date'), 'value' => 'Yii::app()->dateFormatter->format("dd.M.yyyy", $data->date)', ),
Что бы datepicker
работал после ajax
загрузки данных (после поиска, сортировки или постраничного навигатора) необходимо добавить в виджет CGridView следующий параметр:
'afterAjaxUpdate' => 'reinstallDatePicker',
а в конец представления после вызова виджета CGridView:
Yii::app()->clientScript->registerScript('re-install-date-picker', " function reinstallDatePicker(id, data) { $('#Posts_date').datepicker(jQuery.extend({showMonthAfterYear:false},jQuery.datepicker.regional['ru'],{ 'showAnim' : 'fold', 'dateFormat' : 'dd.mm.yy', 'changeMonth' : 'true', 'changeYear' : 'true', 'showButtonPanel' : 'true'})); } ");
Т.к. дата у нас хранится в Unix time нам необходимо подправить метод search()
в модели Posts
. Вместо строки содержащей:
criteria->compare('date', $this->date);
Вставить следующий код:
//$criteria->compare('date', $this->date); if(!empty($this->date) && !empty($this->date)){ $criteria->condition = "date BETWEEN '" . strtotime($this->date) . "' AND '" . (strtotime($this->date) + 86400) . "'"; }
В итоге представление примет вид (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'filter' => $model,
'id' => 'posts-grid', 'afterAjaxUpdate' => 'reinstallDatePicker', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ),
'columns' => array( array( 'name' => 'id', 'filterHtmlOptions' => array('class' => 'user-filter-id'), ), array( 'name' => 'date', 'type' => 'raw', 'filter' => $this->widget('zii.widgets.jui.CJuiDatePicker', array( 'model' => $model, 'attribute' => 'date', 'language' => 'ru', 'options' => array( 'showAnim' => 'fold', 'dateFormat' => 'dd.mm.yy', 'changeMonth' => 'true', 'changeYear' => 'true', 'showButtonPanel' => 'true', ), ), true), 'filterHtmlOptions' => array('class' => 'user-filter-date'), 'value' => 'Yii::app()->dateFormatter->format("dd.M.yyyy", $data->date)', ), array( 'name' => 'header', 'filterHtmlOptions' => array('class' => 'user-filter-header'), ), array( 'name' => 'visibility', 'header' => 'Пуб.', 'filter' => array('' => 'Все', 1 => 'Опубликовано', 0 => 'Не опубликовано'), 'htmlOptions' => array('class' => 'user-filter-visibility', 'style' => 'width:30px;text-align:center'), 'filterHtmlOptions' => array('class' => 'user-filter-visibility'), 'value' => '($data->visibility == 1 ? "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-open\" title=\"Опубликовано\"></span>" : "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-close\" title=\"Не опубликовано\"></span>")', 'type' => 'html' ), array( 'name' => 'userId', 'header' => 'Автор', 'filter' => array('' => 'Все') + CHtml::listData(User::model()->findAll(), 'id', 'name'), 'value' => '$data["user"]["name"];', ), ), ));
Yii::app()->clientScript->registerScript('re-install-date-picker', " function reinstallDatePicker(id, data) { $('#Posts_date').datepicker(jQuery.extend({showMonthAfterYear:false},jQuery.datepicker.regional['ru'],{ 'showAnim' : 'fold', 'dateFormat' : 'dd.mm.yy', 'changeMonth' : 'true', 'changeYear' : 'true', 'showButtonPanel' : 'true'})); } ");
И модель Posts
примет вид:
<?php
class Posts extends CActiveRecord { //... public function search() { $criteria = new CDbCriteria;<p> $criteria->compare('id', $this->id); $criteria->compare('header', $this->header, true); $criteria->compare('text', $this->text, true); $criteria->compare('visibility', $this->visibility); if(!empty($this->date) && !empty($this->date)){ $criteria->condition = "date BETWEEN '" . strtotime($this->date) . "' AND '" . (strtotime($this->date) + 86400) . "'"; } $criteria->compare('userId', $this->userId); return new CActiveDataProvider($this, array( 'pagination' => array( 'pageSize' => 5, ), 'criteria' => $criteria, )); } //... }
После внесения этих изменений виджет будет выглядеть так:
О том как сделать поиск по диапазону дат можно посмотреть здесь: Yii Framework, CGridView поиск по диапазону дат (date range) с помощью виджета datepicker
Кнопки управления записями
Для управления записями (просмотр, редактирование, удаление) нам необходимы кнопки (ссылки и т.д.) добавить их очень просто.
Стандартные кнопки управления
Для добавления стандартных кнопок управления записями необходимо добавить еще одну колонку с кодом:
array( 'class' => 'CButtonColumn', 'template' => {view}{update}{delete}', ),
Свои кнопки управления записями
Часто бывает так, что необходимо изменить стандартный url редактирования, удаления просмотра записи, для этого добавим свои кнопки управления записями. В код виджета CGridView в элемент columns вставим следующий код:
array( 'class' => 'CButtonColumn', 'template' => '<nobr>{view} {edit} {delete}</nobr>', 'buttons' => array( 'view' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-eye-open"></span>', 'imageUrl' => false, 'url' => 'Yii::app()->createUrl("posts/default/detail", array("url"=>$data->url))', ), 'edit' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-pencil"></span>', 'url' => 'Yii::app()->createUrl("posts/edit", array("id"=>$data->id))', ), 'delete' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-remove"></span>', 'imageUrl' => false, 'deleteConfirmation' => 'Are you sure you want to delete this post?', 'url' => 'Yii::app()->createUrl("posts/delete", array("id"=>$data->id))', ), ), ),
В итоге мы получим свои иконки кнопок и url к ним. Так же при нажатии на кнопку удаления будет вызвано окно с подтверждением удаления записи.
В итоге код представления примет следующий вид (файл: /themes/themeName/views/posts/list.php
):
<?php
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'filter' => $model,
'id' => 'posts-grid', 'afterAjaxUpdate' => 'reinstallDatePicker', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ),
'columns' => array( array( 'name' => 'id', 'filterHtmlOptions' => array('class' => 'user-filter-id'), ), array( 'name' => 'date', 'type' => 'raw', 'filter' => $this->widget('zii.widgets.jui.CJuiDatePicker', array( 'model' => $model, 'attribute' => 'date', 'language' => 'ru', 'options' => array( 'showAnim' => 'fold', 'dateFormat' => 'dd.mm.yy', 'changeMonth' => 'true', 'changeYear' => 'true', 'showButtonPanel' => 'true', ), ), true), 'filterHtmlOptions' => array('class' => 'user-filter-date'), 'value' => 'Yii::app()->dateFormatter->format("dd.M.yyyy", $data->date)', ), array( 'name' => 'header', 'filterHtmlOptions' => array('class' => 'user-filter-header'), ), array( 'name' => 'visibility', 'header' => 'Пуб.', 'filter' => array('' => 'Все', 1 => 'Опубликовано', 0 => 'Не опубликовано'), 'htmlOptions' => array('class' => 'user-filter-visibility', 'style' => 'width:30px;text-align:center'), 'filterHtmlOptions' => array('class' => 'user-filter-visibility'), 'value' => '($data->visibility == 1 ? "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-open\" title=\"Опубликовано\"></span>" : "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-close\" title=\"Не опубликовано\"></span>")', 'type' => 'html' ), array( 'name' => 'userId', 'header' => 'Автор', 'filter' => array('' => 'Все') + CHtml::listData(User::model()->findAll(), 'id', 'name'), 'value' => '$data["user"]["name"];', ), array( 'class' => 'CButtonColumn', 'template' => '<nobr>{view} {edit} {delete}</nobr>', 'buttons' => array( 'view' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-eye-open"></span>', 'imageUrl' => false, 'url' => 'Yii::app()->createUrl("posts/default/detail", array("url"=>$data->url))', ), 'edit' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-pencil"></span>', 'url' => 'Yii::app()->createUrl("posts/edit", array("id"=>$data->id))', ), 'delete' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-remove"></span>', 'imageUrl' => false, 'deleteConfirmation' => 'Are you sure you want to delete this post?', 'url' => 'Yii::app()->createUrl("posts/delete", array("id"=>$data->id))', ), ), ), ), ));
Yii::app()->clientScript->registerScript('re-install-date-picker', " function reinstallDatePicker(id, data) { $('#Posts_date').datepicker(jQuery.extend({showMonthAfterYear:false},jQuery.datepicker.regional['ru'],{ 'showAnim' : 'fold', 'dateFormat' : 'dd.mm.yy', 'changeMonth' : 'true', 'changeYear' : 'true', 'showButtonPanel' : 'true'})); } ");
Вид виджета:
Выбор количества записей на страницу
Для удобства сделаем выпадающий список с выбором количества записей на страницу. Для этого действие actionList()
контроллера PostsController
добавим следующий код:
if (isset($_GET['pageSize'])) { Yii::app()->user->setState('pageSize', (int) $_GET['pageSize']); unset($_GET['pageSize']); }
В метод search()
модели Posts
:
public function search() { //... return new CActiveDataProvider($this, array( 'pagination' => array( 'pageSize' => Yii::app()->user->getState('pageSize', 5), ), 'criteria' => $criteria, )); }
В представлении перед виджетом CGridView:
$pageSize = Yii::app()->user->getState('pageSize', 5);
И в виджет CGridView, например, вместо заголовка колонок с кнопками добавим код выпадающего списка с выбором количества элементов на страницу:
'header' => CHtml::dropDownList( 'pageSize', $pageSize, array( 5 => 5, 10 => 10, 20 => 20, 50 => 50, 100 => 100 ), array( 'onchange' => "$.fn.yiiGridView.update('posts-grid',{ data:{pageSize: $(this).val() }})" ) ),
Не забудьте проверить совпадает ли идентификатор (id
) виджета (строка 7) с идентификатором (id
) в строке 10 (posts-grid
).
Итого
В итоге мы имеем следующий код. Код модели Posts
:
<?php
class Posts extends CActiveRecord { //... public function relations() { return array( 'user' => array(self::BELONGS_TO, 'User', 'userId'), ); }
public function search() { $criteria = new CDbCriteria;
$criteria->compare('id', $this->id); $criteria->compare('url', $this->url, true); $criteria->compare('header', $this->header, true); $criteria->compare('text', $this->text, true); $criteria->compare('visibility', $this->visibility); if(!empty($this->date) && !empty($this->date)){ $criteria->condition="date BETWEEN '" . strtotime($this->date) . "' AND '" . (strtotime($this->date) + 86400) . "'"; } $criteria->compare('userId', $this->userId);
return new CActiveDataProvider($this, array( 'pagination' => array( 'pageSize' => Yii::app()->user->getState('pageSize', 5), ), 'criteria' => $criteria, )); } //... }
Код контроллера PostsController
:
<?php
class PostsController extends Controller {
/** * Список записей */ public function actionList() { $this->pageHeader = $this->pageTitle = $this->breadcrumbsTitle = 'Список статей'; $this->breadcrumbs = array( 'Модератор' => '/модератор', $this->breadcrumbsTitle ); $model = new Posts('search'); if (isset($_GET['pageSize'])) { Yii::app()->user->setState('pageSize', (int) $_GET['pageSize']); unset($_GET['pageSize']); } $request = Yii::app()->request->getParam('Posts'); $model->unsetAttributes(); if (isset($request)) { $model->attributes = $request; } $this->render('list', array( 'model' => $model, )); }
}
Код представления:
<?php $pageSize = Yii::app()->user->getState('pageSize', 5);
$this->widget('zii.widgets.grid.CGridView', array( 'dataProvider' => $model->search(), 'filter' => $model,
'id' => 'posts-grid', 'afterAjaxUpdate' => 'reinstallDatePicker', 'htmlOptions' => array('class' => 'table'), 'itemsCssClass' => 'table table-hover table-striped', 'loadingCssClass' => 'grid-preloader',
'pagerCssClass' => 'text-center', 'pager' => array( 'nextPageLabel' => '<span>»</span>', 'prevPageLabel' => '<span>«</span>', 'lastPageLabel' => '<span>»»</span>', 'firstPageLabel' => '<span>««</span>', 'selectedPageCssClass' => 'active', 'hiddenPageCssClass' => 'disabled', 'htmlOptions' => array('class' => 'pagination'), 'class' => 'CLinkPager', 'header' => false, 'pageSize' => 5, ), 'columns' => array( array( 'name' => 'id', 'filterHtmlOptions' => array('class' => 'user-filter-id'), ), array( 'name' => 'date', 'type' => 'raw', 'filter' => $this->widget('zii.widgets.jui.CJuiDatePicker', array( 'model' => $model, 'attribute' => 'date', 'language' => 'ru', 'options' => array( 'showAnim' => 'fold', 'dateFormat' => 'dd.mm.yy', 'changeMonth' => 'true', 'changeYear' => 'true', 'showButtonPanel' => 'true', ), ), true), 'filterHtmlOptions' => array('class' => 'user-filter-date'), 'value' => 'Yii::app()->dateFormatter->format("dd.M.yyyy", $data->date)', ), array( 'name' => 'header', 'filterHtmlOptions' => array('class' => 'user-filter-header'), ), array( 'name' => 'visibility', 'header' => 'Пуб.', 'filter' => array('' => 'Все', 1 => 'Опубликовано', 0 => 'Не опубликовано'), 'htmlOptions' => array('class' => 'user-filter-visibility', 'style' => 'width:30px;text-align:center'), 'filterHtmlOptions' => array('class' => 'user-filter-visibility'), 'value' => '($data->visibility == 1 ? "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-open\" title=\"Опубликовано\"></span>" : "<span aria-hidden=\"true\" class=\"glyphicon glyphicon-eye-close\" title=\"Не опубликовано\"></span>")', 'type' => 'html' ), array( 'name' => 'userId', 'header' => 'Автор', 'filter' => array('' => 'Все') + CHtml::listData(User::model()->findAll(), 'id', 'name'), 'value' => '$data["user"]["name"];', ), array( 'class' => 'CButtonColumn', 'template' => '<nobr>{view} {edit} {delete}</nobr>', 'buttons' => array( 'view' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-eye-open"></span>', 'imageUrl' => false, 'url' => 'Yii::app()->createUrl("posts/default/detail", array("url"=>$data->url))', ), 'edit' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-pencil"></span>', 'url' => 'Yii::app()->createUrl("posts/edit", array("id"=>$data->id))', ), 'delete' => array( 'label' => '<span aria-hidden="true" class="glyphicon glyphicon-remove"></span>', 'imageUrl' => false, 'deleteConfirmation' => 'Are you sure you want to delete this post?', 'url' => 'Yii::app()->createUrl("posts/delete", array("id"=>$data->id))', ), ), 'header' => CHtml::dropDownList( 'pageSize', $pageSize, array( 5 => 5, 10 => 10, 20 => 20, 50 => 50, 100 => 100 ), array( 'onchange' => "$.fn.yiiGridView.update('posts-grid',{ data:{pageSize: $(this).val() }})" ) ), ), ), )); Yii::app()->clientScript->registerScript('re-install-date-picker', " function reinstallDatePicker(id, data) { $('#Posts_date').datepicker(jQuery.extend({showMonthAfterYear:false},jQuery.datepicker.regional['ru'],{ 'showAnim' : 'fold', 'dateFormat' : 'dd.mm.yy', 'changeMonth' : 'true', 'changeYear' : 'true', 'showButtonPanel' : 'true'})); } ");
СSS стили:
.user-filter-id{ width: 30px; } .user-filter-id input{ width: 30px; } .user-filter-date{ width: 110px; } .user-filter-date input{ width: 110px; } .user-filter-visibility select{ width: 40px; } .user-filter-header input{ width: 100%; } .user-filter-visibility input{ width: 30px; } .sort-link.desc{ background:url(/themes/themeName/images/down.gif) right center no-repeat; padding-right: 10px; } .sort-link.asc{ background:url(/themes/themeName/images/up.gif) right center no-repeat; padding-right: 10px; }
Окончательный вид виджета:
Вывод
Виджет CGridView при умелом использовании значительно ускорит разработку и упростит работу с выводом табличных данных, а также сортировку, фильтрацию и навигацию по этим данным.
Дополнительно
Подробный список свойств виджета можно посмотреть на официальном сайте Yii Framework: http://www.yiiframework.com/doc/api/1.1/CGridView
Комментарии
Как поведёт себя подобное решение, если в таблице будет около миллиона записей?
ОтветитьПоиск по записям справится?