📜 ⬆️ ⬇️

Cooking advanced CGridView

This post will be interesting to those who start to get acquainted with the Yii framework, that is, users from the initial to the average level of knowledge of this framework, as well as to those who like to talk about which of the frameworks are cooler.

And so, here I want to show you how flexible and functional you can set up a CGridView, how you can add various filters to it according to relational data, how to add widgets like CJuiDatePicker.

Required Tools
When writing a post, I used Yii Framework 1.1.12 and used MySQL as a DBMS, and therefore all the examples of the above code will be tied to them.

So, let's start?
Consider the situation of a typical blog, we will have the most common sign posts, users, categories of data posts, tags and comments to posts.

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 ; 

')
We’ll look at setting up CgridView on the posts plate, since blog posts are the main entity and all other entities are tied to them.

Customize Relation for the Post Model
 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), ); } 


The controller, we leave almost on default
  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, )); } 


In the Tag model, we will add a new method that will return us an array suitable in format for inserting labels into the filter as a drop-down list.
  public function getForFilter(){ return CHtml::listData( self::model()->findAll(array( 'select' => array('id', 'name') )), 'id', 'name' ); } 


Add a method that returns the full name, in the User model
  public function getFullFio(){ return $this->last_name.' '.$this->first_name.' '.$this->father_name; } 


We need to add a similar method to the Post model to display a list of all tags related to the article.
  public function getTagsToString(){ $t = CHtml::listData( $this->tags, 'id', 'name' ); return implode(',', $t); } 


Further we will work only with the Post model and with the display in which our CGridView is displayed.
To search for tags with relational MANY_MANY, I use an additional field in the post model.
  public $id_tag = ''; 


To filter, we need to extend the Post :: search () method.
I have it looks like this -
  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->addSearchCondition( new CDbExpression( 'CONCAT(author.last_name, " ", author.first_name, " ", author.father_name)' ), $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); return new CActiveDataProvider('Post', array( 'criteria'=>$criteria, 'sort'=>array( 'defaultOrder'=>'t.status, t.update_time DESC', ), )); } 


To display the list of tags in the table, in the form of a single string, we will use the Post :: getTagsToString () magic method we created earlier.
  ... array( 'name'=>'id_tag', 'value'=>'$data->tagsToString', 'filter'=>Tag::model()->forFilter, ), ... 


Similarly, we will do with the mapping of the full name. author of the post
  ... array( 'name'=>'author_id', 'value'=>'isset($data->author) ? $data->author->fullFio : ""' ), ... 


Further more interesting, fasten the standard widget CJuiDatePicker to the filter filter
Add a new column labels with such parameters
  ... 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) ), ... 


But that's not all, if your label uses ajaxUpdate, then the filter with dates will break after updating the label with ajax. To fix this, we need to re-initialize the calendar on the afterAjaxUpdate event;
  ... 'afterAjaxUpdate' => 'function(){ jQuery("#create_date").datepicker({ dateFormat: "yy-mm-dd", changeYear:true }); }', ... 



Customizing the label is also quite easy, for example, if we need to replace an image of some of the buttons or hide them, for standard buttons we can use the properties -
{button_name} ButtonImageUrl
{button_name} ButtonLabel
{button_name} ButtonOptions
{button_name} ButtonUrl

Example: replace the picture of the button buttons of the standard update
  ... array( 'class'=>'CButtonColumn', 'updateButtonImageUrl' => Yii::app()->baseUrl.'/images/configure.gif' ), ... 


or for example we want to create our own button
 ... 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")', ), ), ) ... 


At last
I would like to leave here a few useful links to extensions that will help you with upgrading your CGridView
Allows you to hide the standard view, update and delete buttons for a given criterion
Alphabetical pagination
We display a label in a tree format

Source code dump base inside.

Source: https://habr.com/ru/post/156251/


All Articles