Вложенные критерии в Doctrine 2 Query Builder

В этой статье я расскажу об одном интересном способе формирования вложенных критериев в DQL запросах Doctrine 2.
Последнее время я всё больше занимаюсь разработкой под Doctrine 2. В текущем проекте ORM система вообще не использовалась (ну если не считать Zend_Db и разбросанные то тут, то там куски кода). Поэтому при выборе ORM решил использовать последнюю стабильную версию (2.1 на тот момент).

29 сентября вышла Doctrine 2.2 в которой был исправлен баг с вложенными условиями andx. Суть его в том, что нельзя добавить выражение andx() в условие andWhere(), которео формируется QueryBuilder’ом в Доктрине. Баг пофиксили, и мне пришлось обновить библиотечку. Тут выяснилось что расширение DoctrineExtension\Paginate, которое я использовал, не работает с Doctrine 2.2 потому, что авторы доктрины наконец решили вернуть сделать свой пагинатор (Doctrine\ORM\Tools\Pagination\Paginator). Как говорится, ну где же вы раньше были? 🙂 Вообще я начинаю понимать, почему говорят , что Doctrine 2 в 3 раза быстрее чем Doctrine 1. Они просто выпилили треть функций. Например, больше нет моих любимых $model->fromArray() и $model->toArray(). Авторы предлагают… писать свои реализации. Ну это всё лирика.

Задача

Сегодня будем решать такую задачу. Пусть есть критерий1(a=10) и переменное количество других критериев(criterias). Надо сформировать DQL запрос со следующей Where частью: a=10 AND (criteria[1] OR criterias[2] OR criterias[3] …). Оказывается, что для этого надо поплясать с бубном.

Варианты решения:

  1. Модельный поиск (findBy()) – не подходит по интерфейсу функции.
  2. Native SQL – не подходит, т.к. будет очень много хм.. некачественного кода.
  3. DQL.. вариант, но в лоб не подходит, также как и п.2
  4. QueryBuilder – кажется подходит.

QueryBuilder как раз позволяет легко добавлять части запроса по определённым условиям, т.е. это то, что нужно. Однако есть одна особенность в интерфейсе функции orx() (ну и andx() конечно).

Функция принимает критерии как свои аргументы, и например такой вариант не пойдёт:

Конечно есть и ложка мёда в этой бочке дёгтя. Функция принимает NULL значения и теоретически можно передавать ей аргументы, которые будут через один – NULL. НО в таком случае всё равно надо знать количество криетриев, а если их количество неизвестно – то задача таким методом решена не будет.

Решение

Тут я вспомнил про замечательную функцию PHP call_user_func_array(). Она и позволила написать элегентное решение данной задачи.

Таким образом теперь возможно передавать любое количество критериев в функцию andWhere().

Ссылки

Doctrine 2 Query Builder
Can’t add instance of Andx to QueryBuilder through andWhere
andx nested in another andx