Tutorial for filtering data in CGridview widget

Since Yii 1.1.1, the CGridView widget also supports filtering via table column input fields.

Unfortunately, as a relatively new user to Yii, I have no clue how to implement this feature. I can enable sorting and stuff, but I do not know how to get started with the filters.

Does anybody know how to do this? Or does a tutorial already exist?

Thanks,

Simon

Try generating code with yiic. In Yii 1.1.1 it should produce code with filters implemented.

Thanks!

Did not work in the first place, because I kept my 1.1.0 model when updating.

This did not have the search method.

Now it works! Great improvement.

Hi,

I feel kind of stupid not to figure this out. But I don’t get the filtering thing right. From SimonB’s post I guess I should look in the generated code in the model for a clue. Is the search()-method the trick here?

In my view I’ve got this:


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

		'dataProvider'=>$dataProvider,

 	'columns'=>array('Id', 'FirstName', 'LastName',  'EmailAddress', array('class'=>'CButtonColumn'), ),

 'selectableRows'=>2,

		'filter'=>$model,

));

Sorting works fine, filtering boxes shows up, but no filtering takes place.

What else is required for filtering to work?

Hi,

it’s a little too complicated for my taste, but i’ll summarize here the way searching is used in yiis blog demo.

First of all there is a CGridView inside posts admin view:


<?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'),

        ),

        array(

            'name'=>'create_time',

            'type'=>'datetime',

            'filter'=>false,

        ),

        array(

            'class'=>'CButtonColumn',

        ),

    ),

)); ?>

You can ignore the columns-array at the moment ;)

$model is a ‘Post’ model (you’ll see it inside the controller). As you can see it’s assigned to the filter attribute of CGridView AND the search() function of this model is used as source for the dataProvider.

The filter-property is only used as reference to the model needed for generating ‘active’ form elements in filter cells (like an activeDropDownList instead of a input-element for filtering).

Next let’s take a look inside the ‘Post’ model, where we can find a (new) function called search():


    /**

     * 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);


        return new CActiveDataProvider('Post', array(

            'criteria'=>$criteria,

            'sort'=>array(

                'defaultOrder'=>'status, update_time DESC',

            ),

        ));

    }

This function is used as a source for CGridViews dataProvider so it needs to return an CActiveDataProvider.

The search attributes (here: title and status) are added to the CDbCriteria using compare() and as you can see from e.g. "$this->title" we need to have an instance of your model where we have assigned the search-values as property-values.

This instance is created and filled inside the controller:


    /**

     * Manages all models.

     */

    public function actionAdmin()

    {

        $model=new Post('search');

        if(isset($_GET['Post']))

            $model->attributes=$_GET['Post'];

        $this->render('admin',array(

            'model'=>$model,

        ));

    }

As you see there is no magic in here. We’re creating a Post-object (with scenario ‘search’) and we assign the $_GET[‘Post’]-values (which are the values of the searchfields) as attributes.

Inside the model you may add


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

to rules() to define these attributes to be safe for searching.

That’s it. :)

A little addition: afaik you can not use single side placeholders like “WHERE Name LIKE ‘Foo%’” using $criteria->compare(), but i think it’s very useful.

Therefore i’m using this instead:


if($this->title !== '')

$criteria->addSearchCondition('Title',$this->title.'%',false);

Regards

i have try to use another function instead search into the model, it works retrieving data but filters are death, what have to do to use filters with another function?. I have change ‘search’ with the name of may function in the controller too.

First method:

[list=1][]Define your field (e.g.,‘myField’) in the ‘rules()’ as a valid field for the ‘search’ scenario. This will tell the grid view that it can add a filter.[]Define a setter (setMyField()) and a getter (getMyField()), both referring to a private attribute ($this->_myField).

This will help you regarding the search value. If you do not transform the value, a public attribute should be ok.[*]Up date ‘search()’ to add the required search condition for the field.[/list]

Second method:

Use the extension RelatedSearchBehavior. It may suite your needs.

Example with an related field ‘serial’ where the submitted search value is modified to remove dashes and dots and partial conversion to hex representation. Extra code is required to fully set up RelatedSearchBehavior.

Note that the code also throws an exception in case the getter is used in the wrong scenario.





	public function rules() {

	    $rules = parent::rules();

	    $rules[]=array('serial','safe','on'=>'search');

	    return $rules;

        }


	public function behaviors() {

	    return array(

	            'relatedsearch'=>array(

	                    'class'=>'RelatedSearchBehavior',

	                    'relations'=>array(

	                            'serial'=>array('field'=>'device.device_identifier','searchvalue'=>'serialSearch'),

                             )

                    )

           );

       }

                             

	public function getSerialSearch() {

	    if($this->scenario==='search') {

	        $value = $this->serial;

	        if(preg_match('/^([A-Z]*)([.-\d]*)$/i',$value,$matches)) {

	            $value=implode(unpack('H*',strtoupper($matches[1]))).strtr($matches[2],array('-'=>'','.'=>''));

	        }

	        return $value;

	    } else {

	        throw new Exception('Field to be used only in search scenario');

	    }

	}