Yii: фильтрация в связанных таблицах

При разработке CRUD приложения на yii может возникнуть необходимость фильтрации данных в связанных (объединённых) таблицах. Для формирования табличного представления используется виджет GridView. Столбцы задаются в элементе массива “columns”. Если имя столбика задано с использованием точки (например clientCourse.course_id) фильтрация и сортировка у столбца исчезает. Как её вернуть мы ниже и рассмотрим.

Имеем следующее:

Модели “Client”, “ClientCourse” и “CatalogCourse”.

Файл представления client/index.php, содержащий виджет “TbGridView”.

В файле модели “Client” задаём свойство

public $columnCourse;

В метод rules() вносим изменения в правило валидации, добавляем поле columnCourse:

public function rules()
{
    return array(
        //...

        // The following rule is used by search().
        // Please remove those attributes that should not be searched.
        array('columnCourse, family, name, patronimic, forwho', 'safe', 'on'=>'search'),
    );
}

Таблицы объединены следующим образом:

public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'clientCourse'=>array(self::HAS_MANY, 'ClientCourse', 'client_id'),
'catalogCourses'=>array(self::HAS_MANY, 'CatalogCourse', 'course_id', 'through'=>'clientCourse'),
);
}

Вносим изменения в метод search возращающий data provider необходимый для фильтрации в виджете:

public function search()
{
    $criteria=new CDbCriteria;

    $criteria->with = array('clientCourse');

    // ...

    $criteria->together = true;
    $criteria->compare('clientCourse.course_id', $this->columnCourse);

    return new CActiveDataProvider($this, array(
        'criteria'=>$criteria,
        'pagination'=>array(
            'pageSize'=>20,
        ),
    ));
}

В фрагменте clientCourse – название связи (relations), columnCourse – созданное нами свойство в моделе Client.

Код файла представления приобретает следующий вид:

$this->widget('bootstrap.widgets.TbGridView', array(
    'id'=>'client-grid',
    'type'=>'striped bordered condensed',
    'enableHistory'=>true,
    'template'=>"{items}\n{pager}",
    'dataProvider'=>$model->search(),
    'filter'=>$model,
    'columns'=>array(
        'family',
        'name',
        'patronimic',
        array(
            'type'=>'raw',
            'name'=>'columnCourse',
            'filter'=>CatalogCourse::getAll(),
            'value'=>'CatalogCourse::coursesToStr($data->catalogCourses,$data->clientCourse)',
        ),
        array(
            'class'=>'bootstrap.widgets.TbButtonColumn',
            'htmlOptions'=>array('style'=>'width: 50px'),
            'afterDelete'=>'function(link,success,data){ if(success) $("#statusMsg").html(data); }'
        ),
    ),
));

Из этого файла нас интересует следующий фрагмент описывающий колонку данных:

array(
    'type'=>'raw',
    'name'=>'columnCourse',
    'filter'=>CatalogCourse::getAll(),
    'value'=>'CatalogCourse::coursesToStr($data->catalogCourses,$data->clientCourse)',
),

Имени столбика присваиваем имя свойства, созданного в модели Client. В данном случае качестве фильтра используется выпадающий список, значения для которого возвращаются статическим методом CatalogCourse::getAll().