CGridView filter AJAX pagination (пагинация ломает фильтр)

Общие вопросы по использованию фреймворка. Если не знаете как что-то сделать и это про Yii, вам сюда.
Ответить
chuhlomin
Сообщения: 2
Зарегистрирован: 2012.07.27, 15:55

CGridView filter AJAX pagination (пагинация ломает фильтр)

Сообщение chuhlomin »

Помогите с решнием проблемы, пожалуйста.
AJAX пагинация в CGridView ломает выставленный фильтр.

Действие в контроллере:

Код: Выделить всё

public function actionInfo() {
        $expenses = new Expenses('search');
        $expenses->unsetAttributes();

        if (isset($_GET['Expenses'])) $expenses->attributes = $_GET['Expenses'];
        $this->render('info', array(
            'expenses' => $expenses,
        ));
    }
 
Модель Expenses:

Код: Выделить всё

class Expenses extends CActiveRecord
{
    /* ... */
    public function relations()
    {
        return array(
            'type' => array(self::BELONGS_TO, 'ExpensesTypes', 'type_id'),
        );
    }

    public function search()
    {
        $criteria=new CDbCriteria;
        $criteria->compare('id',$this->id);
        $criteria->compare('type_id',$this->type_id);
        
        $criteria->with('type');

        return new CActiveDataProvider($this, array(
            'criteria' => $criteria
        ));
    }
}
 
Во вьюшке:

Код: Выделить всё

<?php
$this->widget('zii.widgets.grid.CGridView', array(
    'dataProvider' => $expenses->search(),
    'filter' => $expenses,
    'template'=>'{summary}{pager}{items}{pager}', // чтобы и сверху была пагинация
    'columns' => array(
        'id',
        array(
            'name' => 'type_id',
            'value' => '$data->type ? $data->type->title: NULL',
            'filter' =>
                CHtml::activeDropDownList(
                    $expenses, 'type_id',
                    CHtml::listData(ExpensesTypes::model()->findAll(array('order'=>'id ASC')), 'id', 'title')
                )
        )
    ),
));
 
При этом: когда в фильтре в выпадающем меню выбирается конкретный expensesType уходит AJAX-запрос и возвращается нормальная таблица, отфильтрованная по этому type_id.
URL: /?Expenses%5Bid%5D=&Expenses%5Btype_id%5D=1&page=1&ajax=yw2
Пример запроса:

Код: Выделить всё

Expenses[id]:
Expenses[type_id]:1
page:1
ajax:yw2
Если фильтр не выбирать, AJAX-пагинация тоже работает отлично.
URL: /?page=2&ajax=yw2
Пример запроса:

Код: Выделить всё

page:2
ajax:yw2
Но если сначала поставить фильтр, а потом выбрать какую-нибудь страницу, фильтр «пропадает», страница сменяется (таблица, естественно уже не отфильтрована).
URL: /?page=2&ajax=yw2&Expenses%255Bid%255D=&Expenses%255Btype_id%255D=1&
Пример запроса:

Код: Выделить всё

page:2
ajax:yw2
Expenses%255Bid%255D:
Expenses%255Btype_id%255D:1
Как видно, все портит %25. По-видимому, url лишний раз urlencode-ится в CUrlManager createUrl (http://www.yiiframework.com/doc/api/1.1/CUrlManager#createUrl-detail), но как это исправить непонятно.
Аватара пользователя
Ivan Ozercov
Сообщения: 53
Зарегистрирован: 2012.03.21, 15:53
Откуда: Минск

Re: CGridView filter AJAX pagination (пагинация ломает фильт

Сообщение Ivan Ozercov »

может поможет это viewtopic.php?f=3&t=7442
chuhlomin
Сообщения: 2
Зарегистрирован: 2012.07.27, 15:55

Re: CGridView filter AJAX pagination (пагинация ломает фильт

Сообщение chuhlomin »

Мне помогло переопределение CPager, который фильтрует неугодный урл.

Модель Expenses:

Код: Выделить всё

class Expenses extends CActiveRecord
{
    /* ... */

    public function search()
    {
        /* ... */
        return new CActiveDataProvider($this, array(
            'criteria' => $criteria,
            'pagination' => new CPaginationMy
        ));
    }
}
CPaginationMy

Код: Выделить всё

class CPaginationMy extends CPagination {
    public function createPageUrl($controller, $page) {
        $params=$this->params===null ? $_GET : $this->params;
        if($page>0)
            $params[$this->pageVar]=$page+1;
        else
            unset($params[$this->pageVar]);

        $link = $controller->createUrl($this->route, $params);

        $link = str_replace('%255B', '%5B', $link);
        $link = str_replace('%255D', '%5D', $link);

        return $link;
    }
}
tolyan
Сообщения: 61
Зарегистрирован: 2012.05.19, 02:29

Re: CGridView filter AJAX pagination (пагинация ломает фильт

Сообщение tolyan »

По ходу я нашел, не побоюсь этого слова, самое правильное решение этой проблемы.
Демо блог который идет в комплекте yii содержит тот же баг: если добавить в него тьму записей, чтоб заработал пагинатор в CListView, затем ткнуть в любой тэг, а затем на вторую страницу пагинатора то перейдем на совершенно некорректную страницу, содержащю посты всех тэгов, а не выбранного. Решается это настолько просто, что аж обидно =)
у CPagination есть проперти params про которое написано "of parameters (name=>value) that should be used instead of GET when generating pagination URLs."
Вот в него то и надо добавить тэг. Таким образом, добавляем несколько строк в ActIonIndex контроллрера post, после чего он выглядит так:

Код: Выделить всё

    public function actionIndex()
    {
        $criteria=new CDbCriteria(array(
            'condition'=>'status='.Post::STATUS_PUBLISHED,
            'order'=>'update_time DESC',
            'with'=>'commentCount',
        ));
        if(isset($_GET['tag']))
            $criteria->addSearchCondition('tags',$_GET['tag']);

        $dataProvider=new CActiveDataProvider('Post', array(
            'pagination' => array(
                'pageSize'  => Yii::app()->params['postsPerPage'],
                'route'     => 'post/index',
                'pageVar'   => 'pg',
                'params'    => isset($_GET['tag']) ? array('tag' => $_GET['tag']) : array(),
        ),
            'criteria'   => $criteria,
        ));

        $this->render('index',array(
            'dataProvider'=>$dataProvider,
        ));
    }
Опыты показали, что для пагинатора параметы route и params обязаотельны, тогда он дает корректные ссылки и все работает нормально. Если хотя бы один из них не установлен - имеем некорректный урл в ссылках пагинатора.

Добавлено:
продолжил опыты с демо блогом с CDataGrid, который в PostController actionAdmin.
Меняем метод search контроллера Post:

Код: Выделить всё

    public function search()
    {
        $criteria=new CDbCriteria;
        $criteria->compare('title',$this->title,true);
        $criteria->compare('status',$this->status);

    $pgParams = array();
    if (isset($_GET['Post']))
    {
      $pgParams['Post'] = array();
      if (isset($_GET['Post']['title']))
        $pgParams['Post']['title'] = $_GET['Post']['title'];
      if (isset($_GET['Post']['status']))
        $pgParams['Post']['status'] = $_GET['Post']['status'];
    }
    Yii::trace(CVarDumper::dumpAsString($pgParams), "pgparams");
    Yii::trace(CVarDumper::dumpAsString($_GET), "_get");
    
        return new CActiveDataProvider('Post', array(
            'criteria'=>$criteria,
            'sort'=>array(
                'defaultOrder'=>'status, update_time DESC',
            ),
      'pagination' => array(
        'pageSize'  => 10,
        'route'     => 'post/admin',
        'pageVar'   => 'pg',
        'params'    => $pgParams,
      ),  
        ));
    }
и имеем во первых: людские ссылки пагинатора, а во вторых переходы по ссылкам пагинатора не сбивают выставленный фильтр.
Правда есть косяк: вообще не работает совместный фильтр title и status - но это пока решать не буду за ненадобностью
Ответить