Ноябрь 19, 2012
CGridView: отображение, сортировка и фильтрация отношений (relations)

Продемонстрирован один из путей отображения, сортировки и фильтрации связанных данных в CGridView.

Пагинация

Объект CPagination, входящий в состав CActiveDataProvider, добавляет LIMIT и OFFSET к SQL-запросу, который выполняет Yii. Это может стать проблемой, если делаются запросы с наличием JOIN (если установлено together=true для жадной загрузки), потому что база данных возвращает множество строк для одной модели, в то время как Yii ожидает получить одну строку для каждой модели.
Самый простой путь исправить это - сделать группировку по первичным ключам основной модели:

$criteria = new CDbCriteria;
$criteria->with = array('song');
$criteria->group = 't.id';
$criteria->together = true;

Сортировка

Когда в CGridView есть столбец, который не является атрибутом модели, Yii не может автоматически распознать, каким образом производить сортировку. Но мы можем указать это в параметре sort класса CActiveDataProvider.

Во-первых, есть один или более атрибутов во View, которые отображают связанные данные. Эти столбцы имеют атрибут name (например: array('name' => 'song.album')). Мы должны указать Yii, как сортировать атрибут связи song, который называется album.
Нужно добавить атрибут song.album в массив атрибутов в параметре sort. Затем указать, как сортировать этот атрибут по возрастанию и по убыванию. Например, так:

return new CActiveDataProvider($this, array(
    'criteria' => $criteria,
    'sort'=>array(
        'attributes'=>array(
            'song.album'=>array(
                'asc'=>'song.album',
                'desc'=>'song.album DESC',
            ),
            '*',
        ),
    ),
));

Фильтрация

Этот случай немного более сложен в реализации. Представим фильтры в верхней части CGridView как обычные поля <input> - так, как-будто они созданы при помощи CHtml::activeTextField($review, 'review'). Безусловно, для текстовых полей требуется $model в качестве первого параметра и имя атрибута в качестве второго.

Мы собираемся базировать поле <input> фильтра на связанной модели. Преимущество этого заключается в том, что сохраняется дефолтная функциональность Yii, такая как валидация поля.

Во-первых, в контроллере мы создаем модель для столбца со связанными данными: $song = new Song('search');
Затем, очищаем ее атрибуты также, как мы делаем это для основной модели:
$song->unsetAttributes();

Теперь у нас есть переменная модели $song, которую мы можем использовать в activeTextField. Мы должны передать эту переменную во View. Используем для этого более элегантный подход и поместим переменную $song в свойство главной модели Review. Чтобы это сделать, для начала нужно объявить свойство в модели Review: public $searchSong;
Вернувшись в контроллер, помещаем модель Song в созданное свойство: $review->searchSong = $song;
Во View мы создаем столбец и определяем в нем фильтр:


array(
    'name' => 'song.name',
    'filter' => CHtml::activeTextField($review->searchSong, 'name'),
),

Мы поместили модель Song в первый параметр, и атрибут этой модели во второй параметр. До сих пор всё идет хорошо. Если мы перезагрузим страницу, то увидим поле <input> и сможем ввести в него данные. Но если мы нажмем Enter, то выполнится submit в контроллер.

В контроллере мы должны перехватить данные, которые были посланы и поместить их в модель $song. Сделаем это таким же образом, как и для главной модели:

if (isset($_GET['Song'])) {
    $song->attributes = $_GET['Song'];
}

Теперь перейдем в то место, где непосредственно происходит поиск для CGridView, метод главной модели, который предоставляет DataProvider для CGridView (обычно, это $model->search()).
Здесь мы просто добавляем дополнительное $criteria->compare() для столбца, по которому хотим производить фильтрацию. Мы используем модель внутри свойства searchSong, после того, как добавили искомое значение ранее в контроллере.

$criteria->compare('song.name', $this->searchSong->name, true);

Таким образом можно организовать отображение, сортировку и фильтрацию связанных данных в CGridView.
Теги: ,
Добавить комментарий:
Комментариев: (5)
Рейтинг: 1 Опубликовал Вазирхан 3922 дн. назад
Не нравится Нравится
что такое Review: public $searchSong;
Рейтинг: 0 Опубликовал admin 3918 дн. назад
Не нравится Нравится
В модели Review нужно объявить public атрибут $searchSong.
Рейтинг: 0 Опубликовал mixartemev 3747 дн. назад
Не нравится Нравится
Гениально! Спасибо!
Рейтинг: 0 Опубликовал mixartemev 3746 дн. назад
Не нравится Нравится
А можно, пожалуйста, и код контроллера написать?
Рейтинг: 2 Опубликовал admin 3746 дн. назад
Не нравится Нравится
Да, контроллер будет так примерно выглядеть: http://pastebin.com/PiCjxeqv
Опубликовать