remember filters for CGridView

How to remember filters between navigation on CGridView[color=#1C2837][size=2]?[/size][/color]

[color=#1C2837][size=2]

[/size][/color]

[color=#1C2837][size=2]I would like to store the filters the user has set per gridview. Also I would like to have a reset button. [/size][/color]

[color=#1C2837][size=2]Is this inbulit? If not how can I do it?[/size][/color]

This does not remember filter when I navigate to other page and come back to the page. I have ajax active for the grid.

then you use session variables

Is this functionality built in? Is there an extension for this kind of problem?

For the reset button check this post - http://www.yiiframework.com/forum/index.php?/topic/14242-modifying-cgridviews-default-filtersearch-behaviour/page__view__findpost__p__70802

And what about saving/restoring values into filters?

I did not completely understand your need for saving/restoring…

Do you want to "remember" the filter settings even if the user goes to some other webpage that is not your… in this case you would need to remember that in a database…

If you want to remember it only while the user is logged on your site… than you need to use sessions as suggested by Mahdi…

Anyway this functionality is not built in… you need to implement the saving of the filter values… and then in $model->search() you need to retreive that values if they exist… and apply them…

Session is ok for use.

Now I am thinking if I should come up with a behavior class for this, could you help me if it’s possible for the usage and if so answer the following questions:

  • where do I place behaviors classes?

  • how do I do/attach a behavior to be used by the search method?

  • how do I make sure that CGridView uses the behavior, so it retrieves and populates back the values from session?

  • how can the behavior also populate the _form.php files (probably subscribing into the CActiveForm or the model?) what options do I have here?

You can make a behavior if you need to use this in many different CGridViews… the main problem is how the behavior will know what fields to check… how many there are… and so on…

If you want to go with the behavior here is a start:

Let’s say we want to call this behavior mysearch… you need to create a file mysearch.php… you can put it in the protected/components folder

mysearch.php




<?php

class mySearch extends CActiveRecordBehavior

{

	public $field1;


	public function preSearch()

	{

		$this->owner->{$this->field1}='Pr';

	}

}



Then in the model you attach the behavior and define the field like:




	public function behaviors(){

		return array(

			'mySearch'=>array(

				'class'=>'application.components.mySearch',

				'field1'=>'name',  //.. name is the attribute from the model

			),

		);

	}



Finaly in the model search method you add the call to the preSearch() method like:




	public function search()

	{

		$this->preSearch();

		$criteria=new CDbCriteria;

    	...



This way by calling preSearch() we are setting the name attribute to "Pr"

Thanks I will see what can I make from it.

The system will know the fields based on the ‘safe’ flag in the rules.

I was thinking on the behavior method… that’s why I added to it the field1… that in the model should be set to the actual attribute you need to set… like ‘name’

It should be automatic, without referencing the properties of a model.

I would image to take automatically the properties from the model based on the safe flag defined in the rules.

How does this sound to you?

Just after posting I was thinking about this…

In the preSearch() you can use


$attributes=$this->owner->getSafeAttributeNames();

So the $attributes is an array of safe attributes…

I just tried to make this working… and here is the complete solution

First we create the behavior - mysearch.php - put it in protected/components




<?php

class mySearch extends CActiveRecordBehavior

{

	public function readSearchValues()

	{

		$modelName=get_class($this->owner);

		$attributes=$this->owner->getSafeAttributeNames();


		foreach($attributes as $attribute)

		{

			if(null!=($value=Yii::app()->user->getState($modelName.$attribute,null)))

			{

				$this->owner->$attribute=$value;

				Yii::app()->user->setState($modelName.$attribute,1,1);

			}

		}

	}


	public function saveSearchValues()

	{

		$modelName=get_class($this->owner);

		$attributes=$this->owner->getSafeAttributeNames();

		foreach($attributes as $attribute)

			Yii::app()->user->setState($modelName.$attribute,$this->owner->$attribute);

	}

}



In the model you need to attach the behavior




public function behaviors(){

	return array(

		'mySearch'=>array(

			'class'=>'application.components.mySearch',

		),

    );

}



And in the controller we decide if to read or to save the values… like




public function actionAdmin()

{

	$model=new Company('search');

	$model->unsetAttributes();  // clear any default values

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

	{

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

		$model->saveSearchValues();

	}

	else

		$model->readSearchValues();


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

		'model'=>$model,

	));

}



Thanks. Awesome job.

Now some comments on bugs/missing features

1). If I reload the pages twice or more (without navigation away), the page looses the values

2). In the safe enumeration I have values from relations. like `address1 and I get Exception when the behavior class wants to access it.

How can I make sure all relations have been loaded before trying to access the values from them?

3). Can I attach this behavior more generally ? Like use of afterConstruct and to skip the step needed in [font="monospace"][size="2"]actionAdmin(). This question should read: can I move into the behavior class definition, the code present in actionAdmin to handle save/read values?[/size][/font]

I have given you a working solution… now you can adjust it as you need it… :D

1 - for me it’s best to use the saved value just once… and delete it as soon as it’s retreived… if you don’t want this… just remove the line


Yii::app()->user->setState($modelName.$attribute,1,1);

2 - the behavior class is called from actionAdmin() after the model is created… so everything should be set already… this would need debugging to see what is the real cause of the error

3 - this behavior needs to be called only for an action that renders a CGridView and you need to decide when to save the values to SESSION and when to read them…

can't see a way to make this generally... additionaly CGridView does not fire any event...

For 2) that property exists in a relation.

But that relation is not loaded.

address1 is in safe list because it is used by an alias





public function search() {

        $criteria = new CDbCriteria;

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

        $criteria->with = array('residence' => array('select' => 'address1'));


        return new CActiveDataProvider(get_class($this), array(

            'criteria' => $criteria,

        ));

}

As you see in the code the address1 is actually $this->residence->address1 and this is listed in the ‘safe’ enumeration because needs to get input. If I don’t list address1 in the safe list, it doesn’t get the input.

so how do I load all the relations and check this against it?

for 3)

I had to add to the behavior this:




public function afterConstruct($event) {

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

            $this->owner->unsetAttributes();


            if (isset($_GET[get_class($this->owner)])) {

                $this->owner->attributes = $_GET[get_class($this->owner)];

                $this->owner->saveSearchValues();

            } else {

                $this->owner->readSearchValues();

            }

        }

    }

What I see is this line:


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

So $this->residence_id is the attribute that gets the input in the CGridView filter… and that attribute should be on the safe validator list… I don’t see why would address1 be on that list if it’s not used for massive assignment.

3 - good one… didn’t think of that…

Sorry, you are right, but I’ve added to be able to use with the behavior.

Do you have some idea how to save the input for that kind of situation?

I did not understand you here… I still don’t see why would you put a rule for a related attribute… that should be put in the related model…