Содержание статьи:

Зачем нужен 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 в действие 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