Using filters with CGridView and CArrayDataProvider

10 followers

Using filters on CGridView with CActiveDataProvider is easy, but with CArrayDataProvider it is a bit tricky.

To use the filters you have to create a separate model class. I used some code from the following forum topic: link

Here is some example code:

Model

Copy and include this class into your application.

/**
 * Filterform to use filters in combination with CArrayDataProvider and CGridView
 */
class FiltersForm extends CFormModel
{
    public $filters = array();
 
    /**
     * Override magic getter for filters
     */
    public function __get($name)
    {
        if(!array_key_exists($name, $this->filters))
            $this->filters[$name] = null;
        return $this->filters[$name];
    }
 
    /**
     * Filter input array by key value pairs
     * @param array $data rawData
     * @return array filtered data array
     */
    public function filter(array $data)
    {
        foreach($data AS $rowIndex => $row) {
            foreach($this->filters AS $key => $value) {
                // unset if filter is set, but doesn't match
                if(array_key_exists($key, $row) AND !empty($value)) {
                    if(stripos($row[$key], $value) === false)
                        unset($data[$rowIndex]);
                }
            }
        }
        return $data;
    }
}

Controller

// Create filter model and set properties
$filtersForm=new FiltersForm;
if (isset($_GET['FiltersForm']))
    $filtersForm->filters=$_GET['FiltersForm'];
 
// Get rawData and create dataProvider
$rawData=User::model()->findAll();
$dataProvider=new CArrayDataProvider($rawData);
 
// Render
$this->render('index', array(
    'filtersForm' => $filtersForm,
    'dataProvider' => $dataProvider,
));

View

$columns = array(
    array(
        'header'=>CHtml::encode('Name'),
        'name'=>'username',
        'value'=>'$data->username',
    ),
    array(
        'header'=>CHtml::encode('Organisation'),
        'name'=>'organisation',
        'value'=>'$data->organisation',
    ),
);
 
$this->widget('zii.widgets.grid.CGridView', array(
'id'=>'area-grid',
'dataProvider'=>$dataProvider,
    'columns'=>$columns,
    'filter'=>$filtersForm,
));

Total 3 comments

#7671 report it
abdul.alief at 2012/04/08 04:03am
minor fix on FiltersForm Class

i use Chessspider approach, but with minor modification on the filter() function to refresh dataprovider rawData so the pagination will be correct and filter can be perform nicely as we want to.

so here's the filter() function

/**
     * Filter input array by key value pairs
     * @param CarrayDataProvider $data
     * @return CarrayDataProvider Filtered data array
     */
    public function filter(CArrayDataProvider $data)
    {
        $temp = $data->rawData;
 
        foreach ($temp AS $index => $item)
        {
            foreach ($this->filters AS $key => $value)
            {
                if($value == '') continue; // bypass empty filter
 
                $test = false;  // value to test for
 
                if($item instanceof CModel)
                {
                    if(isset($item->$key) == false ) throw new CException("Property ".get_class($item)."::{$key} does not exist!");
                    $test = $item->$key;
                }
                elseif(is_array($item))
                {
                    if(!array_key_exists($key, $item)) throw new CException("Key {$key} does not exist in Array!");
                    $test = $item[$key];
                }
                else
                    throw new CException("Data in CArrayDataProvider must be an array of arrays or CModels!");
 
                if(stripos($test, $value) === false)
                    unset($temp[$index]);
            }
        }
 
        $data->rawData = array_values($temp);
        return $data;
    }
#5501 report it
ChessSpider at 2011/10/17 04:56am
Updated your code..

I had to modify the filter() method to this:

/**
     * Filter input array by key value pairs
     * @param CarrayDataProvider $data
     * @return CarrayDataProvider Filtered data array
     */
    public function filter(CArrayDataProvider $data)
    {
        $temp = $data->getData();
 
        foreach ($temp AS $index => $item)
        {
            foreach ($this->filters AS $key => $value)
            {
                if($value == '') continue; // bypass empty filter
 
                $test = false;  // value to test for
 
                if($item instanceof CModel)
                {
                    if(isset($item->$key) == false ) throw new CException("Property ".get_class($item)."::{$key} does not exist!");
                    $test = $item->$key;
                }
                elseif(is_array($item))
                {
                    if(!array_key_exists($key, $item)) throw new CException("Key {$key} does not exist in Array!");
                    $test = $item[$key];
                }
                else
                    throw new CException("Data in CArrayDataProvider must be an array of arrays or CModels!");
 
                if(stripos($test, $value) === false)
                    unset($temp[$index]);
            }
        }
 
        $data->setData(array_values($temp));
        return $data;
    }

and the controller Render-call to this:

$this->render('missingcomputers', array(
            'dataProvider' => $filtersForm->filter($dataProvider),
            'filtersForm' => $filtersForm,
        ));
#5454 report it
ChessSpider at 2011/10/13 11:13am
Can't get it to work..

I copied it over 1:1, literally. Yet it still refuses to do any filtering whatsoever.

It shows all data, and the filter box appears. However when I enter text in it, no filtering occurs. There is no change whatsoever.

Any ideas what could be the problem?

Leave a comment

Please to leave your comment.