Имеется модель (класс, наследник CActiveRecord), соответствующая некоторой таблице реляционной базы данных.
Задача — сделать так, чтобы при поиске элементов в данной таблице они изначально отфильтровывались по какому-либо критерию.
Поясню на примере. В таблице user хранятся пользователи различных типов: студенты, преподаватели, абитуриенты (тип задается значением поля role таблицы: student, prepod, abiturient). Необходимо, чтобы все возможные запросы на выборку данных из этой таблицы через данный класс изначально работали только со студентами.
Для решения данной задачи в Yii предусмотрен удобный механизм Scope, который позволяет на уровне класса для запросов задавать подобные предварительные условия.
Вот некоторые примеры кода, демонстрирующие данную возможность.
1. Scope по умолчанию
В класс-модель добавляем метод defaultScope. В нем задаем те предварительные условия, которые требуются
1 2 3 |
public function defaultScope() { return array('condition'=>"role='student'"); // возвращаем массив условий, которые должны применяться. В массиве, кроме condition, могут использоваться и другие элементы, используемые в yii-запросах: order, limit и т.д. } |
Теперь при обращениях к объектам этого класса мы всегда будем работать только со студентами, например
1 |
$students = Users::model()->findAll("active=1"); |
вернет всех активных студентов (без активных преподавателей и абитуриентов).
Если требуется отменить фильтр по умолчанию (по какой-либо причине), необходимо воспользоваться в запросах методом resetScope(). Пример:
1 |
$students = Users::model()->resetScope()->findAll("active=1"); // вернет всех активных пользователей (и студентов, и абитуриентов, и преподавателей) не смотря на то, что в defaultScope задано условие "только студенты" |
2. Именованные Scope
Кроме Scope по умолчанию, вы можете задать любое число специфичных фильтров. Они задаются через переопределение метода scopes.
Пример, который реализует три фильтра: students, prepods и abiturients
В класс-модель добавляем метод scopes:
1 2 3 4 5 6 7 8 |
public function scopes() { return array( 'students'=>array('condition'=>"role='student'"), 'prepods'=>array('condition'=>"role='prepods'"), 'abiturients'=>array('condition'=>"role='abiturients'"), ); } |
Как используем? Добавляем фильтр в запрос (предполагается, что метод defaultScope из пункта 1 данного поста не задан, иначе запросы 2 и 3 вернут пустое значение не смотря на наличие записей в таблице):
1 2 3 |
$students = Users::model()->students()->findAll("active=1"); // все активные студенты $prepods= Users::model()->prepods()->findAll("active=1"); // все активные преподаватели $abiturients = Users::model()->abiturients()->findAll("active=1"); // все активные абитуриенты |
И напоследок еще несколько интересных возможностей фильтрации.
— В одном запросе могут использоваться сразу несколько фильтров.
Например, если к добавленным в п. 2 фильтрам добавить фильтр only10, который будет ограничивать вывод только 10-ю первыми записями
можно будет выполнять следующие запросы
1 |
$students = Users::model()->students()->only10()->findAll("active=1"); // не более 10 активных студентов из последних добавленных |
— существует альтернативный способ создания дополнительного фильтра, который позволяет использовать параметры при вызове фильтра
Для этого необходимо добавить в класс — модель метод с уникальным именем (именем фильтра) следующего содержания:
1 2 3 4 5 6 7 8 |
public function onlyXY($limit, $order) { $this->getDbCriteria()->mergeWith(array( 'limit' => $limit, 'order' => $order )); return $this; } |
Вызов будет таким
1 |
$students = Users::model()->students()->onlyXY(10, 'id desc')->findAll("active=1"); // не более 10 активных студентов из последних добавленных |
В отличие от предыдущего примера, сейчас мы можем сразу при вызове задавать параметры для функции фильтрации (число возвращаемых записей, например). Иногда это может оказаться полезным.