CGridView - Filtering and loosing Sort Informations

Hi!

First question here, so please be patient with me :)

We are using the CGridView(s) for filtering and sorting data - the problem is, that the sort-information gets lost after the filter values change and I have no clue where to start looking for the problem, so perhaps someone of you can provide help.

I changed the blog-demo a little bit to demonstrate the problem (see code below, it contains a Belongs-To relation, and I am able to sort over the values - am proud of that, because the documentation of that is a bit weak in my opinion):

[list=4]

[*] Go to manage-post view

[*] Sort one column

[*] Enter filtervalue, e.g. ‘Wel’ in ‘Title’

[*] You will recognize, that the sort order is lost…

[/list]

You may modify the following two files to test the behaviour:

  • views/post/admin.php:



<?php

$this->breadcrumbs=array(

	'Manage Posts',

);

?>

<h1>Manage Posts</h1>


<?php $this->widget('zii.widgets.grid.CGridView', array(

	'dataProvider'=>$model->search(),

	'filter'=>$model,

	'columns'=>array(

		array(

			'name'=>'title',

			'type'=>'raw',

			'value'=>'CHtml::link(CHtml::encode($data->title), $data->url)'

		),

		array(

			'name'=>'status',

			'value'=>'Lookup::item("PostStatus",$data->status)',

			'filter'=>Lookup::items('PostStatus'),

		),

             [i]array(

			'name'=>'authorName',

			'type'=>'raw',

			'value'=>'$data->author->username',

		),[/i]

                array(

			'class'=>'CButtonColumn',

		),

	),

)); ?>



  • models/Post.php:



<?php

class Post extends CActiveRecord

{

        ...

	private $_oldTags;

     [i]public $authorName;[/i]

        ...

	public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			array('title, content, status', 'required'),

			array('status', 'in', 'range'=>array(1,2,3)),

			array('title', 'length', 'max'=>128),

			array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/', 'message'=>'Tags can only contain word characters.'),

			array('tags', 'normalizeTags'),


		     [i]array('title, status, authorName', 'safe', 'on'=>'search'),[/i]

		);

	}

         

        ...


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'Id',

			'title' => 'Title',

			'content' => 'Content',

			'tags' => 'Tags',

			'status' => 'Status',

			'create_time' => 'Create Time',

			'update_time' => 'Update Time',

		     [i]'authorName' => 'Author',[/i]

		);

	}


        ...


	/**

	 * Retrieves the list of posts based on the current search/filter conditions.

	 * @return CActiveDataProvider the data provider that can return the needed posts.

	 */

	public function search()

	{

		$criteria=new CDbCriteria;


		$criteria->compare('title',$this->title,true);


		$criteria->compare('status',$this->status);

		

	     [i]$criteria->compare('author.username', $this->authorName);


		$sort = new CSort();

		$sort->attributes = array(

			'title',

			'status',

			'authorName' => array(

			    'asc'=>'author.username',

    			'desc'=>'author.username DESC',

				),

		

		);

		$sort->defaultOrder = 'status, update_time DESC';

		$criteria->with = array('author');

		

		return new CActiveDataProvider('Post', array(

			'criteria'=>$criteria,

			'sort'=>$sort,

		));[/i]

	}

}



Does someone have an idea where to tackle the problem? Is it even possible to do in the current version [we are using trunk]?

Thanks in advance and kind regards,

Rudi

This is expected behavior (it’s not your problem). It is so because we want to avoid accumulating GET parameters in the URLs (each time a new filter is specified, it appends the filter condition to the URL). We will consider resolving this issue in the near future.

Thank you for your fast reply and the information provided.

We really like the framework, keep on with it, good work!

Regards,

Rudi

I just fixed this problem. Please clean up your assets directory and try again. Thanks.

sorry for the stupid questio… i’m using 1.1.0 version… where is the filter attrubute in the documentation of CGridView?

Not a stupid question, there aren’t :)

Simply there isn’t anything about it in doc to 1.1.0, because this feature was added on 20 January to the trunk (thats currently the planned release 1.1.1)

And Thanks Qiang! Saw your reply and am trying it out right now!

Edit: Works, great!

@[member=‘Rudi’]

Could you share a bit more regarding your implementation of ‘sorting and filtering’ with CGridView?

  • What code have you in the PostController?

  • Specifically, how do you send in $model from the action method? Is it simply a ‘new’ Post object?

  • Where are you doing pagination?

  • A screen-shot of the result would be cool (I know… loads of questions, but I am working on exactly the same topic - only I try to get the result back via Ajax using renderPartial…)

Many thanks!

Hi,

I am going to post the complete admin-view to the blog demo where sorting with the authors name is possible by Friday at the latest. To your questions:

  • I solved the problem using the given CGridView, I did not have to code an extension or something on my own. Sorting and filtering is possible with the framework.

  • Pagination and the returned models are delivered in a CActiveDataProvider with a specified CDbCriteria $criteria and a CSort $sort - I am going to show that in the blog-example.

Regards, Rudi

Hi,

as promised my solution to display the authorname in the admin-view of the manage-page in the blog example.

I did not have to modify much code, because of that I post only the necessary modifications - or just download the attachment that contains all code of the modified blog demo.

  • views/post/admin.php (Notice access to $data->author->username via relational mapping and access to the models search method in the ‘dataProvider’ field):

[html]

<?php

$this->breadcrumbs=array(

'Manage Posts',

);

?>

<h1>Manage Posts</h1>

<?php $this->widget(‘zii.widgets.grid.CGridView’, array(

'dataProvider'=&gt;&#036;model-&gt;search(),


'filter'=&gt;&#036;model,


'columns'=&gt;array(


	array(


		'name'=&gt;'title',


		'type'=&gt;'raw',


		'value'=&gt;'CHtml::link(CHtml::encode(&#036;data-&gt;title), &#036;data-&gt;url)'


	),


	array(


		'name'=&gt;'status',


		'value'=&gt;'Lookup::item(&quot;PostStatus&quot;,&#036;data-&gt;status)',


		'filter'=&gt;Lookup::items('PostStatus'),


	),


	array(


		'name'=&gt;'authorName',


		'type'=&gt;'raw',


		'value'=&gt;'&#036;data-&gt;author-&gt;username',


	),


	


	array(


		'class'=&gt;'CButtonColumn',


	),


),

)); ?>

[/html]

  • models/Post.php (Notice the search-method, the building of the criteria and the construction of the sort object - I only post the modified methods of Post):



<?php


class Post extends CActiveRecord

{

	/**

	 * The followings are the available columns in table 'tbl_post':

	 * @var integer $id

	 * @var string $title

	 * @var string $content

	 * @var string $tags

	 * @var integer $status

	 * @var integer $create_time

	 * @var integer $update_time

	 * @var integer $author_id

	 */

	const STATUS_DRAFT=1;

	const STATUS_PUBLISHED=2;

	const STATUS_ARCHIVED=3;


	private $_oldTags;


        // transient variable to be able to search and sort over it

	public $authorName;


        // A lot of code: ...bla bla bla bla.....




	/**

	 * @return array validation rules for model attributes.

	 */

	public function rules()

	{

		// NOTE: you should only define rules for those attributes that

		// will receive user inputs.

		return array(

			array('title, content, status', 'required'),

			array('status', 'in', 'range'=>array(1,2,3)),

			array('title', 'length', 'max'=>128),

			array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/', 'message'=>'Tags can only contain word characters.'),

			array('tags', 'normalizeTags'),


			array('title, status, authorName', 'safe', 'on'=>'search'),

		);

	}


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'Id',

			'title' => 'Title',

			'content' => 'Content',

			'tags' => 'Tags',

			'status' => 'Status',

			'create_time' => 'Create Time',

			'update_time' => 'Update Time',

			'authorName' => 'Author',

		);

	}


	/**

	 * Retrieves the list of posts based on the current search/filter conditions.

	 * @return CActiveDataProvider the data provider that can return the needed posts.

	 */

	public function search()

	{

		$criteria=new CDbCriteria;


		$criteria->compare('title',$this->title,true);


		$criteria->compare('status',$this->status);

		

		$criteria->compare('author.username', $this->authorName, true);


		$sort = new CSort();

		$sort->attributes = array(

			'title',

			'status',

			'authorName' => array(

                            'asc'=>'author.username',

                            'desc'=>'author.username DESC',

			),

		

		);

		$sort->defaultOrder = 'status, update_time DESC';

		$criteria->with = array('author');

		

		return new CActiveDataProvider('Post', array(

			'criteria'=>$criteria,

			'sort'=>$sort,

		));

	}

}



That is all that is to do, there has to be no modification of the controllers code of the blog demo. Now you are able to sort and filter the grid as you like. That was all functionality we needed and use it without problems… We are even using it to navigate deep into the relations (e.g. post.author.address.street.number).

Perhaps it is possible to put the code above into the blog-demo? I don’t know whom to ask, it would really help to clear some more things up in the demo.

Hi,

I used the CGridView in my sample application. I need to display the Gender as shown below.

The logic what I need is that if the Gender value is 1 then "Male" will be displayed; otherwise "Female".

I always get "Female" data. When I check my data, the data in the Customers_Gender is "1".

I appreciate your advice.

<?php $this->widget(‘zii.widgets.grid.CGridView’, array(

'id'=&gt;'ts-customers-grid',


'dataProvider'=&gt;&#036;model-&gt;searchCompleteCustomer(),


'filter'=&gt;&#036;model,


'columns'=&gt;array(


	'customers_id',


	array(


		'name'=&gt;'customers_gender',


		'type'=&gt;'raw',


		'value'=&gt; customers_gender === '1' ? 'male':'female', // Issue is here. 


	),				


	'customers_firstname',


	'customers_lastname',		


	'customers_email_address',

try


'value'=>'$data->customers_gender?"male":"female"',

Thanks Liang.

Now I found another problem in my CGridView.

I have 2 models (Customer and AddressBook).

In the Customer model, these fields are included:

customers_id, customers_firstname, customers_lastname

In the AddressBook model, these fields are included:

customers_id, company, city

I could show the two fields in the CGridView. Please see my code.

<?php $this->widget(‘zii.widgets.grid.CGridView’, array(

'id'=&gt;'ts-customers-grid',


'dataProvider'=&gt;&#036;model-&gt;searchCompleteCustomer(),


'filter'=&gt;&#036;model,


'columns'=&gt;array(


	'customers_id',						


	'customers_firstname',


	'customers_lastname',		


	'customers_email_address',


    array(


		'name'=&gt;'addressBook.company',


        'type'=&gt;'raw',


        'value'=&gt;'&#036;data-&gt;addressBook-&gt;company',


    ),


    array(


		'name'=&gt;'addressBook.city',


        'type'=&gt;'raw',


        'value'=&gt;'&#036;data-&gt;addressBook-&gt;city',


    ),


    


	array(


		'class'=&gt;'CButtonColumn',


	),


),

)); ?>

Now I need to filter the two fields (Company and City).

How can I filter for these two fields?

I appreciate your reply.

Thanks

Win Naung

I’m having same problem. I could’t get filter to display and run for “relation.field” fields.

Seems that there is not built-in support for filtering and sorting by foreign columns yet. One solution here: http://www.mrsoundle…-CGridView.aspx. Not just a few lines of code though.