Skip to content

Ru:criteria

stev edited this page Apr 21, 2011 · 28 revisions

Criteria

(это еще не законченная статья, пожалуйста, не надо в нее вставлять куски больших непонятных примеров, без пояснений, со всякими вспомогательными классами Debug не относящимися к onPHP и т.п. Однако подправка логических ошибок, опечаток, корректировка фраз и т.д. приветствуется ;) )

Введение

Criteria - класс, позволяющий получать список бизнес объектов, единичный объект, списки полей полей объектов или производные полей. В отличие от просто OSQL Criteria больше оперирует в условиях не полями таблицы, а именно property'ями объектов.

При создании Criteria как правило в нее передается DAO класса, список экземпляров которого необходимо получить. В определенных случая DAO при создании Criteria сразу можно не задавать, а добавить позже через метод setDao. Если этого не сделать, то Criteria не сможет сформировать OSQL.

Простейшее получение списка всех бизнес-объектов класса MyBook:

<?php
Criteria::create(Actor::dao())->getList();
?>

SQL запрос в базу при этом будет следующий:

SELECT id, name, country, childs, best_film_id FROM actor;

Expressions

Критерии поддерживают возможность выборки объектов по параметрам. Для этого в критерию через метод add нужно добавить какой-либо LogicalObject. Например необходимо получить всех актеров, живущих в США:

<?php
Criteria::create(Actor::dao())->
	add(Expression::eq('country', DBValue::create('USA')))->
	getList();
?>

Запрос в базу будет иметь вид:

SELECT id, name, country, childs, best_film_id FROM actor WHERE country = 'USA';

При добавление в Criteria нескольких LogicalObject в SQL отношение между ними определяется через логический оператор AND


Так же в условиях выборки объектов могут участвовать параметры связанных объектов. Например, можно получить список всех актеров у которых лучший фильм был титаник:

<?php
Criteria::create(Actor::dao())->
	add(Expression::eq('bestFilm.name', DBValue::create('Titanic')))->
	getList();
?>

SQL запрос:

SELECT id, name, country, childs, best_film_id
FROM actor
JOIN film ON actor.best_film_id = film.id
WHERE film.name = 'Titanic';

OrderBy

Список объектов можно получать отсортированным по условиям. Для этого у Criteria используется метод addOrder в который обычно передается объект OrderBy, либо строка, которая оборачивается в OrderBy. Для примера можно получить всех актеров начинающихся с буквы J и отсортировать их пол алфавиту:

<?php
Criteria::create(Actor::dao())->
	add(Expression::like('name', DBValue::create('J%')))->
	addOrder(OrderBy::create('name'))->
	getList();
?>

SQL запрос:

SELECT id, name, country, childs, best_film_id FROM actor WHERE name like 'J%' ORDER BY name;

При использовании нескольких условий сортировки в SQL порядок этих условий соответствует порядку добавления соотвествющих условий в Criteria

Projection и необъектные выборки

Бывают случаи когда необходимо получить не объект, а лишь несколько полей или какие-то агрегатные значения от этих полей. В таких случаях используется метод addProjection. Например посчитать число ролей во всех фильмах, вышедших в 2009 году.

<?php
$rolesCount2009 = Criteria::create(Actor::dao())->
	addProjection(Projection::count('id', 'count'))->
	add(Expression::eq('roles.film.year', DBValue::create('2009')))->
	getCustom('count');
?>

SQL запрос:

SELECT count(actor.id) as "count"
FROM actor
LEFT JOIN role ON actor.id = role.actor_id
LEFT JOIN film ON role.film_id = film.id
WHERE film.year = '2009';

Для более сложного, комбинированного примера поставим задачу - получить имена всех актеров у которых было в 2010 году больше трех ролей и отсортируем их по двум условиям - 1) по убыванию числа ролей 2) по алфавиту:

<?php
$countFunction = SQLFunction::create('count', 'id');
$nameList = Criteria::create(Actor::dao())->
	addProjection(Projection::property('name', 'name'))->
	addProjection(Projection::group('name'))->
	addProjection(
		Projection::having(
			Expression::gt(
				$countFunction,
				DBValue::create(3)
			)
		)
	)->
	addProjection(Projection::count('id', 'count'))->
	add(Expression::eq('roles.film.year', DBValue::create('2009')))->
	addOrder(OrderBy::create($countFunction)->desc())->
	addOrder(OrderBy::create('name'))->
	getCustomList();
?>

SQL запрос:

SELECT
actor.name AS "name", count(actor.id) AS "count"
FROM "actor"
LEFT JOIN role ON role.actor_id = actor.id
LEFT JOIN film ON role.film_id = film.id
WHERE film.year = '2009'
GROUP BY actor.name
HAVING (count(actor.id) > '3')
ORDER BY count(actor.id) DESC, actor.name

Limit, Offset и getResult

У Criteria есть возможность задавать Limit и Offset для запросов через методы setLimit и setOffset соотвественно. Работает это ожидаемо и дописывает в конце SQL запросы LIMIT X OFFSET Y. Для получения объектных списков, порой, удобней использовать не getList, а getResult. В отличие от получения просто списка этот метод возвращает объект QueryResult который заполняется двумя данными - список объектов по выборке и общее число объектов (count) в выборке, если бы не было Limit и Offset. Это удобно, например, когда нужно получить комментарии для какой-то страницы, но при этом нужно знать общее число комментариев для отрисовки пейджера или просто для его отображения.


Далее отдельной темой будет рассмотрено то, Как работает критерия


Примечания:

  1. Запросы в базу упрощены, из них убраны кавычки и алиасы. В реальных запросах генерируемых через критерию каждой таблице задается уникальный алиас и все используемые поля таблиц имеют алиас.
  2. Стоит подсмотреть выдуманную мету для объектов из примеров. Мета,объекты и структура базы не претендуют на продуманность, а лишь спроектированы таким образом, что бы примеры были наиболее наглядны и просты.
Clone this wiki locally