Вложенные критерии в 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

8 Comments

  1. Спасибо, удобно. Работаю с Doctrine не так давно, в своем блоге я выкладываю переводы глав документации по Doctrine 2. Возможно это поможет кому-нибудь:
    http://odiszapc.ru/doctrine/

  2. Спасибо! Очень ценный материал! А то я было начал разочаровываться в Doctrine.

      1. Интересный материал, спасибо. Никогда не использовал expr(), но сейчас обязательно попробую.
        P.S. Ссылка на анекдот не работает

        1. О, редирект отвалился. Спасибо, что заметили, Алексей. Поправил!)

  3. $queryBuilder = $this->getEntityManager()->createQueryBuilder();

    $orConditions = $queryBuilder->expr()->orX();
    $andConditions = $queryBuilder->expr()->andX();

    $orConditions->add(‘g.key = :value1’);
    $orConditions->add(‘g.key = :value2’);

    $andConditions->add($orConditions);

    $queryBuilder->andWhere($andConditions);

    $queryBuilder->setParameter(‘value1’, 5);
    $queryBuilder->setParameter(‘value2’, 20);

Leave a Comment