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

// Февраль 15th, 2012 // Doctrine 2

В этой статье я расскажу об одном интересном способе формирования вложенных критериев в 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() конечно).

$qb->andWhere($qb->expr()->orx($criterias[1], $criterias[2] $criterias[3]));

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

$qb->andWhere($qb->expr()->orx($criterias));

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

Решение

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

public function getDQLForSomething() {
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('g.key, c.a')
   ....
   ->where('c.a = 10');
$criterias = array();
if($something)
            $criterias[] = $qb->expr()->andx(
                        $qb->expr()->gte('g.key', '5'),
                        $qb->expr()->lte('g.key', '20')
            );
if($something2)
            $criterias[] = $qb->expr()->gt('g.key', '20');
$where = \call_user_func_array(array($qb->expr(), "orx"), $criterias);
$qb->andWhere($where);
$q = $qb->getQuery();
return $q;
}

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

Ссылки

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

Share

Спасибо!


Если вам помогла статья, или вы хотите поддержать мои исследования и блог - вот лучший способ сделать это:


7 Responses to “Вложенные критерии в Doctrine 2 Query Builder”

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

  2. Leonard:

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

  3. brizzz:

    Чётко, как раз аналогичная задача возникла

  4. test:

    $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);

Комментировать