Данный пост будет интересен тем кто начинает знакомится с Yii framework, то есть пользователям от начального до среднего уровня знаний данного фреймворка, а так же тем кто любит похоливарить на тему какой из фреймворков круче.
И так, я здесь хочу вам показать насколько гибко и функционально можно настроить CGridView, как можно добавлять в нее различные фильтры по реляционным данным, как добавлять виджеты такие как CJuiDatePicker.
Необходимые инструменты
При написание поста, я использовал Yii Framework 1.1.12 и в качестве СУБД использовал MySQL и по этому все примеры приведенного кода будут привязаны к ним.
Ну что начнем?
Рассмотрим ситуацию типичного блога, у нас будет самая обычная табличка постов, пользователей, категорий данных постов, тегов и комментариев к постам.
CREATE TABLE IF NOT EXISTS `tbl_comment` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`content` text COLLATE utf8_unicode_ci NOT NULL,
`status` int(11) NOT NULL,
`create_time` int(11) DEFAULT NULL,
`author` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(128) COLLATE utf8_unicode_ci DEFAULT NULL,
`post_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_comment_post` (`post_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `tbl_post` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`content` text COLLATE utf8_unicode_ci NOT NULL,
`status` int(11) NOT NULL,
`create_time` datetime NOT NULL,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`author_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK_post_author` (`author_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1;
CREATE TABLE IF NOT EXISTS `tbl_posts_tags` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_post` int(11) NOT NULL,
`id_tag` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `tbl_tag` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`frequency` int(11) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `tbl_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`first_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`last_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`father_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`salt` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(128) COLLATE utf8_unicode_ci NOT NULL,
`profile` text COLLATE utf8_unicode_ci,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=2 ;
Мы будим рассматривать настройку CgridView, на табличке постов, поскольку посты в блоге являются основной сущностью и все остальные сущности привязаны к ним.
Настраиваем реляции для модели Post
public function relations(){
return array(
//Теги
'tags' => array(self::MANY_MANY, 'Tag', 'tbl_posts_tags(id_post, id_tag)'),
//Автор поста
'author' => array(self::BELONGS_TO, 'User', 'author_id'),
//Комментарии
'comments' => array(self::HAS_MANY, 'Comment', 'post_id', 'condition'=>'comments.status='.Comment::STATUS_APPROVED, 'order'=>'comments.create_time DESC'),
//Подсчет количества комментариев
'commentCount' => array(self::STAT, 'Comment', 'post_id', 'condition'=>'status='.Comment::STATUS_APPROVED),
);
}
Контроллер мы оставим практически по дефолту
public function actionAdmin(){
$model=new Post('search');
if(isset($_GET['Post'])){
$model->setAttributes($_GET['Post'], false);
$model->id_tag = isset($_GET['Post']['id_tag']) ? $_GET['Post']['id_tag'] : '';
}
$this->render('admin',array(
'model'=>$model,
));
}
В модель Tag мы добавим новый метод который будет возвращать нам массив подходящий по формату для вставки в фильтр таблички в качестве выпадающего списка
public function getForFilter(){
return CHtml::listData(
self::model()->findAll(array(
'select' => array('id', 'name')
)), 'id', 'name'
);
}
Добавим метод возвращающий полное Ф.И.О., в модель User
public function getFullFio(){
return $this->last_name.' '.$this->first_name.' '.$this->father_name;
}
<source>
Подобный метод нам нужно добавить в модель Post для отображения списка всех тегов которые относятся к статье
<source lang="PHP">
public function getTagsToString(){
$str = '';
$i = 0;
foreach($this->tags as $tag){
$str .= ($i ? ', ' : '') . $tag->name;
$i++;
}
return $str;
}
<source>
Дальше мы будим работать только с моделью Post и с отображением в котором у нас выводится наша CGridView.
Для поиска по тегам с реляцией MANY_MANY я использую дополнительное поле в модели постов
<source lang="PHP">
public $id_tag = '';
Для фильтрации мы должны расширить метод Post::search().
У меня он выглядит так —
public function search($defaultCriteria = null){
$criteria = $defaultCriteria != null ? $defaultCriteria : new CDbCriteria;
//Указываем какие реляции нам нужно доставать при создании таблички
//для облегчения запроса мы не будим тащить все поля, мы укажем какие поля нам нужны для поиска и отображения
$criteria->with = array(
'tags' => array(
'select' => array('id', 'name')
),
'author' => array(
'select' => array('last_name', 'first_name', 'father_name')
),
'commentCount'
);
$criteria->together = true;
if(!empty($this->author_id)){
//В фильтр id_author у нас есть возможность писать любой критерий поиска по имени, фамилии или отчеству
$criteria->condition = (!empty($criteria->condition) ? $criteria->condition : '').
'author.last_name LIKE :aid
OR author.first_name LIKE :aid
OR author.father_name LIKE :aid
OR CONCAT(author.last_name, " ", author.first_name, " ", author.father_name) LIKE :aid';
if(!count($criteria->params)){
$criteria->params = array();
}
$criteria->params[':aid'] = '%'.$this->author_id.'%';
}
//составляем критерий запроса
if(isset($this->id_tag) && !empty($this->id_tag)){
$criteria->compare('tags.id', '='.$this->id_tag, true);
}
$criteria->compare('t.id', '='.$this->id, true);
$criteria->compare('t.create_time', '='.$this->create_time, true);
$criteria->compare('t.update_time', '='.$this->update_time, true);
$criteria->compare('t.title', $this->title, true);
$criteria->compare('t.status', '='.$this->status);
print_r($criteria);
return new CActiveDataProvider('Post', array(
'criteria'=>$criteria,
'sort'=>array(
'defaultOrder'=>'t.status, t.update_time DESC',
),
));
}
Для вывода списка тегов в табличке, в виде одной строки мы воспользуемся созданным нами ранее магическим методом Post::getTagsToString()
...
array(
'name'=>'id_tag',
'value'=>'$data->tagsToString',
'filter'=>Tag::model()->forFilter,
),
...
Аналогично мы сделаем с отображением полного Ф.И.О. автора поста
...
array(
'name'=>'author_id',
'value'=>'isset($data->author) ? $data->author->fullFio : ""'
),
...
Далее интереснее, прикручиваем стандартный виджет CJuiDatePicker к фильтру таблички
Добавляем новую колонку таблички с такими параметрами
...
array(
'name' => 'create_time',
'type' => 'raw',
'htmlOptions' => array('align' => 'center', 'style' => 'width: 123px;'),
'filter' => $this->widget('zii.widgets.jui.CJuiDatePicker', array(
'model' => $model,
'id' => 'create_time',
'attribute' => 'create_time',
'htmlOptions' => array('style' => 'width: 80px;'),
'options' => array(
'dateFormat' => 'yy-mm-dd',
'changeYear' => true
),
), true)
),
...
Но это еще не все, если ваша табличка использует ajaxUpdate то фильтр с датами будет ломаться после обновления таблички с помощью ajax. Что бы исправить это мы должны по событию afterAjaxUpdate пере инициализировать календарик, делается это так
...
'afterAjaxUpdate' => 'function(){
jQuery("#create_date").datepicker({
dateFormat: "yy-mm-dd",
changeYear:true
});
}',
...
Кастомизация таблички происходит тоже достаточно легко, например если нам нужно заменить изображение какой то из кнопочек или скрыть их, для стандартных кнопочек мы можем использовать свойства —
{button_name}ButtonImageUrl
{button_name}ButtonLabel
{button_name}ButtonOptions
{button_name}ButtonUrl
пример заменим картинку кнопки стандартной кнопки update
...
array(
'class'=>'CButtonColumn',
'updateButtonImageUrl' => Yii::app()->baseUrl.'/images/configure.gif'
),
...
или например мы хотим создать свою кнопочку
...
array(
'class' => 'ext.myButtonColumn',
'template'=> '{on} {off} ',
'buttons' => array(
'off' => array(
'label' => 'Активировать',
'imageUrl' => Yii::app()->baseUrl.'/images/cancel.gif',
'visible' => '$data->active == 0',
'url' => 'Yii::app()->createAbsoluteUrl("post/on")',
),
'on' => array(
'label' => 'Деактивировать',
'imageUrl' => Yii::app()->baseUrl.'/images/flag.gif',
'visible' => '$data->active == 1',
'url' => 'Yii::app()->createAbsoluteUrl("post/off")',
),
),
)
...
Напоследок
Я бы хотел оставить здесь несколько полезных ссылок на экстеншены которые помогут вам с апгдейдом ваших CGridView
Позволяет скрывать стандартную кнопку delete по заданному критерию
Алфавитная пагинация
Отображаем табличку в древовидном формате
Автор: Igogo2012