Yii 1.1: CGridView keep state of page and sort.

14 followers

Remember-filters-gridview is one of the must-use extension on all of my project. However, it lacks of storing the page and sort variables of the grid view.

In addition, Page size dropdown is also useful that I hope some day it can squeeze into the core of the CGridView.

Based on the Page size dropdown, I would like to try to write a wiki on how to store the page and sort variables on the similar way to the Page size dropdown.

First, in your controller (my example is ExtractController), make it like this:

public function actionAdmin() {
        $model = new Extract('search');
 
        // This is as suggested on the Remember-filter-gridview, 
        // to clear the state of the filter. 
        // I add four line to clear the page and sort as well.
        // Note: somehow, I need to put those lines above the 
        // EButtonColumnWithClearFilters, otherwise, it will not clear the 
        // Extract_page and Extract_sort states.
        if (intval(Yii::app()->request->getParam('clearFilters')) == 1) {
            Yii::app()->user->setState('Extract_page', null);
            unset($_GET['Extract_page']);
            Yii::app()->user->setState('Extract_sort', null);
            unset($_GET['sort']);
            EButtonColumnWithClearFilters::clearFilters($this, $model); //where $this is the controller
        }
 
        // This portion of code is belongs to Page size dropdown.
        $pageSize = Yii::app()->user->getState('extractPageSize', Yii::app()->params['defaultPageSize']);
 
        if (isset($_GET['pageSize'])) {
            $pageSize = (int) $_GET['pageSize'];
            Yii::app()->user->setState('extractPageSize', $pageSize);
            unset($_GET['pageSize']);
        }
 
        // I achieve similar result to Page size with the Extract_page.
        // ELSE body is needed to handle the differences of case back to first page from
        // other pages and call to admin page from anywhere. The ajax method is the key to
        // differentiate it. Call to admin would not be an ajax call, while the call from 
        // pagination is an ajax call (by default. since you can make it non ajax as well).
        if (isset($_GET['Extract_page'])) {
            $extractPage = (int) $_GET['Extract_page'] - 1;
            Yii::app()->user->setState('Extract_page', $extractPage);
            unset($_GET['Extract_page']);
        } else if (isset($_GET['ajax'])) {
            Yii::app()->user->setState('Extract_page', 0);
        }
 
        // Sort is little bit different. Do not UNSET the sort variable 
        // since it will be directly used by the CGridView.
        // The ELSE body is used to handle non ajax calls to admin page.
        if (isset($_GET['sort'])) {
            $extractSort = $_GET['sort'];
            Yii::app()->user->setState('Extract_sort', $extractSort);
        } else if(Yii::app()->user->hasState('Extract_sort')) {
            $_GET['sort'] = Yii::app()->user->getState('Extract_sort');
        }
 
        $this->render('admin', array(
            'model' => $model,
            'pageSize' => $pageSize,
        ));
    }

Second, the model (Extract.php). We only need to change the search method :

public function search() {
        $criteria = new CDbCriteria;
 
        $criteria->compare('name', $this->name, true);
        $criteria->compare('productNumber', $this->productNumber, true);
        $criteria->compare('specification', $this->specification, true);
 
        $sort = new CSort;
        $sort->defaultOrder = 'name ASC';
        $sort->attributes = array(
            'name' => 'name',
            'productNumber' => 'productNumber',
            'specification' => 'specification',
        );
 
        $sort->applyOrder($criteria);
 
        return new CActiveDataProvider($this, array(
                    'criteria' => $criteria,
                    'sort' => $sort,
                    'pagination' => array(
                         // This line below is the only addition made to the model by Page size dropdown.
                        'pageSize' => Yii::app()->user->getState('extractPageSize', Yii::app()->params['defaultPageSize']),
                        // This line below is the only addition made to the model in
                        // choosing the page to be displayed.
                        'currentPage' => Yii::app()->user->getState('Extract_page', 0),
                    ),
                ));
    }

That's all we should do.

Future works The code in actionAdmin is little bit messy to me, can anyone give some feedback to tidy up little bit? In addition, I am a beginner in PHP and Yii!, I don't quite understand the consequences in term of performance and security to store those variables in state.

I admit that this is not purely my idea, but still I would like to share this to get some feedback as well as help others with some problem with me.

Total 5 comments

#18194 report it
Daniel at 2014/09/24 06:08pm
Re: And how about this approach?

@rackycz Thank you for your feedback. Unfortunately, I could not comment to your suggestion since I am still not getting the idea on how you keep sort and filter the gridview. Can you elaborate more on simple working example? Thanks in advance.

#18186 report it
rackycz at 2014/09/24 02:18am
And how about this approach?

As I already wrote many times, I'm friend of very simple native solutions. What do you think about this?

http://www.yiiframework.com/wiki/462/yii-for-beginners-2/#hh10

@Daniel: The idea is very simple. I just save the whole GET variable to session. And if user wants to display the same grid again and does not specify GET parameters, the old are used.

Because filter, pagination and sorting are just data sent via GET request in format:

web.com/controller/action?Model[attribute]=filterValue&Model_sort=columName&Model_page=PageNr
#7969 report it
yiqing95 at 2012/04/30 06:20am
@adinugro you can extend your own one

i just extend the BootGridView class ,and do some extra thing . but this do not affect to the EPageSize just use it with CGridView and CListView .

if you want make it displayed in {summary} section , just rewrite the renderSummary function ,it's not so difficult doing that .

just give you a reference , it 's not ready for publish ^-^ (you see i am Chinese ,and not ready use the English for my own codes ):

<?php
/**
 * Created by JetBrains PhpStorm.
 * User: yiqing
 * Date: 12-2-16
 * Time: 上午11:16
 * To change this template use File | Settings | File Templates.
 */
Yii::import('ext.bootstrap.widgets.BootGridView');
class EBootGridView extends BootGridView
{
 
    protected $rowSetKeys = array();
 
    public $rowIdPrefix = 'gr_';
 
    public function init()
    {
        parent::init();
 
        $this->rowSetKeys = $this->dataProvider->getKeys();
    }
 
    /**
     * @param $row
     * ad row id property to the generated tr element
     */
    public function renderTableRow($row)
    {
        $rowId = $this->rowIdPrefix . $this->rowSetKeys[$row];
 
        if ($this->rowCssClassExpression !== null) {
            $data = $this->dataProvider->data[$row];
            echo '<tr class="' . $this->evaluateExpression($this->rowCssClassExpression, array('row' => $row, 'data' => $data)) . '" id="' . $rowId . '">';
        }
        else if (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0)
            echo '<tr class="' . $this->rowCssClass[$row % $n] . '" id="' . $rowId . '">';
        else
            echo '<tr id="' . $rowId . '">';
        foreach ($this->columns as $column)
            $column->renderDataCell($row);
        echo "</tr>\n";
    }
//----------------------------<总是在template中可用listPager>---------------------------------------
    /**
     * @var array
     * config  for the CListPager
     */
    public $listPager = array();
 
    /**
     * @return mixed
     *  using  EBootGridView::listPager config to render the listPager
     */
    public function renderListPager()
    {
        if (!$this->enablePagination)
            return;
 
        $pager = array();
        $class = 'CListPager';
        if (is_array($this->listPager)) {
            $pager = $this->listPager;
            if (isset($pager['class'])) {
                unset($pager['class']);
            }
        }else{
            throw new CException('the listPager must be an array ,which is a config array for the Class CLinkPager');
        }
        $pager['pages'] = $this->dataProvider->getPagination();
        if(!isset($pager['htmlOptions']) || ! isset($pager['htmlOptions']['onchange'])){
           //if  not  set the onchange ,the default behavior is  ajax pagination
           $pager['htmlOptions']['onchange'] = "if(this.value!=''){\$.fn.yiiGridView.update('{$this->getId()}',{url:this.value}); /*window.location=this.value;*/};";
        }
        if ($pager['pages']->getPageCount() > 1) {
            //echo '<div class="' . $this->pagerCssClass . '">';
            $this->widget($class, $pager);
            //echo '</div>';
        }else{
            $this->widget($class, $pager);
        }
    }
    //----------------------------<总是在template中可用listPager/>-----------------------------------------
 
    //.........<允许在模板中使用epageSize 扩展>.................................................
 
     public $ePageSize = array();
 
    /**
     * @return mixed
     *  using  EBootGridView::listPager config to render the listPager
     * ------------------------------------------------
     * 有bug  原因未知 待查.....
     * ------------------------------------------------
     */
    public function renderEPageSize()
    {
        if(!$this->enablePagination)
            return;
 
        $widget = array();
        $class = 'ext.PageSize.EPageSize';
 
        if(is_string($this->ePageSize)){
            $class = $this->ePageSize ;
        } else if(is_array($this->ePageSize)){
            $widget = $this->ePageSize;
            if(isset($widget['class']))
            {
                $class = $widget['class'];
                unset($widget['class']);
            }
        }
        $widget['gridViewId'] = $this->getId();
        $widget['pageSize'] = Yii::app()->request->getParam('pageSize',null);
 
        if(! isset($widget['defaultPageSize'])){
            $widget['defaultPageSize'] = 10;
        }
        if(! isset($widget['pageSizeOptions'])){
          $widget['pageSizeOptions'] =  array(5=>5, 10=>10, 25=>25, 50=>50, 75=>75, 100=>100);
        }
        /*
        $this->widget('ext.PageSize.EPageSize', array(
            'listViewId' => '<?php echo $this->class2id($this->modelClass); ?>-grid',
            'pageSize' => Yii::app()->request->getParam('pageSize',null),
            'defaultPageSize' =>  10 ,   // may use this :  Yii::app()->params['defaultPageSize'],
            'pageSizeOptions'=> array(5=>5, 10=>10, 25=>25, 50=>50, 75=>75, 100=>100), // you can config it in main.php under the config dir . Yii::app()->params['pageSizeOptions'],// Optional, you can use with the widget default
        ));
        */
 
        $pagination = $this->dataProvider->getPagination();
        if($pagination->getPageCount()>1){
            $this->widget($class,$widget);
        }
    }
 
    //.........<允许在模板中使用epageSize 扩展/>................................................
    //----------<松弛模板 在模板中可用用上面配置到的键 >--------------------------------------------------------------------------------------------
 
    /**
     * @var array
     * -----------------------
     * 参考CButtonColumn::buttons
     * -----------------------
     */
    public $templates = array();
 
    /**
     * Renders a section.
     * This method is invoked by {@link renderContent} for every placeholder found in {@link template}.
     * It should return the rendering result that would replace the placeholder.
     * @param array $matches the matches, where $matches[0] represents the whole placeholder,
     * while $matches[1] contains the name of the matched placeholder.
     * @return string the rendering result of the section
     * ------------------------------------------------------------
     * 拷贝自CBaseListView
     * -------------------------------------------------------------
     */
    protected function renderSection($matches)
    {
            $method='render'.$matches[1];
            if(method_exists($this,$method))
            {
                $this->$method();
                $html=ob_get_contents();
                ob_clean();
                return $html;
            }elseif(! empty($this->templates) && is_array($this->templates)){
               $widgetTemplateName = $matches[1]; //template Token
                if(isset($this->templates[$widgetTemplateName])){
                  $widgetConfig =   $this->templates[$widgetTemplateName];
                    return $this->_renderWidgetTemplate($widgetConfig);
                }
            }
            return $matches[0];
    }
 
  /**
   * @param array $widgetConfig
   * @return mixed
   * @throws CException
   */
    protected function _renderWidgetTemplate($widgetConfig=array()){
       if(isset($widgetConfig['class'])){
           $widgetClass = $widgetConfig['class'];
           unset($widgetConfig['class']);
          return $this->widget($widgetClass,$widgetConfig,true);
       }else{
           throw  new CException('you must specify the widget \'s class for using the templates  ');
       }
    }
    //----------<松弛模板 在模板中可用用上面配置到的键/ >--------------------------------------------------------------------------------------------
}
#7847 report it
Daniel at 2012/04/22 06:05pm
Re: there are extensions you can use for pageSize Selection

Thank you @yiqing95 for the information. I am trying to update this wiki to include your suggestion. One thing how can I include the widget into the BootGridView ( I want to put the widget in the Summary text of the BootGridView)? I saw the example in your readme for BootGridView, it seems you use EBootGridView and no where I can see you put the widget.

Thanks,

Daniel

#7818 report it
yiqing95 at 2012/04/19 09:52pm
there are extensions you can use for pageSize Selection

EListView

and here PageSize

and my modified version :EPageSize in github

it's possible to use this extension without touch the action and you model class ^-^ just do some works in your view file

Leave a comment

Please to leave your comment.

Write new article