Поиск и сортировка связанных моделей в CGridView

Оригинал — Searching and sorting by related model in CGridView

Допустим, у нас есть две модели и отношения между ними:
class Author extends CActiveRecord {
...
}
 
class Post extends CActiveRecord {
...
    function relations() {
        return array(
            'author'=>array( self::BELONGS_TO, 'Author', 'id_author' ),
        );
    }
...
}
в списке сообщений, вы хотели бы показать имя автора в колонке, позволить сортировать по этой колонке и, вероятно, дать возможность отфильтровать по подстроке имени автора. Лучшее решение (на мой взгляд), чтобы обеспечить все эти возможности:

Сначала вы должны добавить новый атрибут модели Post, в которой строка поиска будет сохранена. Вы можете использовать столбец внешнего ключа для достижения этого же эффекта. Вы должны пометить новый атрибут как «safe» в правилах валидации.
class Post extends CActiveRecord {
  public $author_search;
  ...
  public function rules() {
    return array(
      ...
      array( 'xxx,yyy,author_search', 'safe', 'on'=>'search' ),
    );
  }
}
Now we have to use this attribute in search criteria (in standard implementation — there is a search() function in every model). Also we have to specify that we want to fetch Post models together with related Author by setting 'with' attribute of criteria (this way there will be only one database query with join instead of many queries fetching related authors in lazy load mode):
Теперь мы должны использовать этот атрибут в критерии поиска (в стандартной реализации — есть search() в каждой модели). Кроме того, мы должны указать, что мы хотим получить модели Post вместе с соответствующими Author, установив 'with' атрибут критериям (таким образом, там будет выполнен только один запрос к базе данных с JOIN вместо нескольких ленивых запросов выборки авторов):
$criteria = new CDbCriteria;
$criteria->with = array( 'author' );
...
$criteria->compare( 'author.username', $this->author_search, true );

Теперь мы можем добавить еще одну хитрую функцию CActiveDataProvider:
return new CActiveDataProvider( 'Post', array(
    'criteria'=>$criteria,
    'sort'=>array(
        'attributes'=>array(
            'author_search'=>array(
                'asc'=>'author.username',
                'desc'=>'author.username DESC',
            ),
            '*',
        ),
    ),
));
параметр «attributes» из раздела «sort» конфигураций позволяет нам перегружать поиск. Эта конфигурация говорит, что, когда пользователь хочет отсортировать список по «author_search» поле это должно быть сделано, как указано. Звезда ('*') в конце списка означает, что все другие атрибуты этой модели должны обрабатываться в штатном режиме. Таким образом, вы можете изменить сортировку атрибутов по умолчанию (например, указать, что, когда пользователь сортирует по last_name колонка должна быть отсортирована по last_name и first_name вместе).

В итоге наш CGridView будет выглядеть следующим образом:
$this->widget('zii.widgets.grid.CGridView', array(
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'columns'=>array(
        'title',
        'post_time',
        array( 'name'=>'author_search', 'value'=>'$data->author->username' ),
        array(
            'class'=>'CButtonColumn',
        ),
    ),
));
Вот и все. Мы отсортировали по имени пользователя вместо id_user и у нас есть поиск по подстроке имя пользователя.

3 комментария

avatar
Не перестает меня удивлять active record, жалко только он такой требовательный к ресурсам, в высоконагруженном проекте его не получится использовать.
avatar
А вот и не правда. Не такой уж он и требовательный.
avatar
Ну впринципе можно допилить к активрекорду lazy load.

Оставить комментарий