Как использовать сложные критерии в хранилище сущности Doctrine 2? - PullRequest
34 голосов
/ 18 марта 2011

Допустим, у меня есть таблица с информацией о фестивалях.
У каждого фестиваля есть дата начала и окончания.

Я хочу выбрать все фестивали, которые проводятся (которые происходят) в определенный день.
Это значит, что я хочу выбрать все фестивали, для которых дата начала - до или в определенный день, а дата окончания - после или в тот же день.

Итак, я пошел в класс репозитория фестивальной сущности и создал метод для этого.
Но аргумент критерия «findBy» ожидает, что это массив, который во всех примерах рассматривается только как простой критерий (например, «array ('name' => 'billy')" выберет все строки, которые имеют значение billy в своих имя столбца), который использует только оператор сравнения.

Как я могу использовать другие операторы, такие как

>, <, !=, IN, NOT IN, LIKE    

и т. Д.?

Спасибо

Ответы [ 4 ]

86 голосов
/ 19 апреля 2013

В Doctrine 2.3 добавлен метод match () , который позволяет использовать Критерии .

Пример Джереми Хикса может быть написан так (обратите внимание, что вместо массива возвращается ArrayCollection).

public function findActiveFestivals($start, $end)
{
    $expr = Criteria::expr();
    $criteria = Criteria::create();
    $criteria->where($expr->gte('start', $start));
    $criteria->andWhere($expr->lte('end', $end);
    return $this->matching($criteria);
}

Лично я бы не стал использовать и где-то здесь, а использоватьеще несколько строк для улучшения читабельности, например:

public function findActiveFestivals($start, $end)
{
    $expr = Criteria::expr();
    $criteria = Criteria::create();
    $criteria->where(
      $expr->andX(
        $expr->gte('start', $start),
        $expr->lte('end', $end)
      )
    );
    return $this->matching($criteria);
}

Использовать предложение IN очень просто.

public function findFestivalsByIds($ids)
{
    $expr = Criteria::expr();
    $criteria = Criteria::create();
    $criteria->where($expr->in('id', $ids));
    return $this->matching($criteria);
}

Класс Criteria находится в Doctrine, который не совсем ORM-илиПространство имен DBAL Common, как и их ArrayCollection (который поддерживает критерии больше, чем EntityRepository).

Это означает, что код без репозитория не может быть использован для создания сфокусированных критериев.Так что должно быть хорошо использовать этот класс за пределами хранилища.QueryBuilder в последнее время также поддерживает критерии .Таким образом, даже при создании более сложных запросов, требующих QueryBuilder, вы можете использовать Criteria для обеспечения гибкости кода, не относящегося к базе данных, в том, что он запрашивает.

21 голосов
/ 18 марта 2011

Вам нужно написать собственный запрос (возможно, с использованием DQL), если вы хотите что-то конкретное. Я считаю, что встроенные методы «findBy» больше подходят для быстрого захвата объектов, если у вас есть менее конкретные критерии. Я не знаю ваших имен сущностей или где они хранятся. Может быть что-то вроде этого в качестве функции в вашем хранилище фестиваля.

public function findActiveFestivals($start, $end)
{
    $qb = $this->_em->createQueryBuilder();
    $qb->select('f')
        ->from('Festival', 'f')
        ->where('f.start >= :start')
        ->andWhere('f.end <= :end')
        ->setParameters(array('start' => $start, 'end' => $end));

    return $qb->getQuery()->getArrayResult();
}
10 голосов
/ 27 августа 2011

это не ответ на вопрос о доктрине Дорон, есть хранилище сущностей, которое вообще не заставляет нас использовать запрос ...

$this->em->getRepository($this->entity)->findBy(array $criteria);

, но он спросил, как сложный оператор в массиве $ критерий нормального форматамассива $criteria есть array('field'=> $value);

9 голосов
/ 08 марта 2015

У меня была такая же проблема некоторое время назад, когда мои хранилища Doctrine стали очень уродливыми из-за сложных запросов. Мне также пришлось конвертировать Yii ActiveRecord (с объектами Criteria) в Doctrine, а в Doctrine в то время не было объектов Criteria.

Я нашел пост в блоге Бенджамина Эберлей , в котором есть интересное решение этой проблемы на основе шаблона спецификации .

Это дает вам возможность отложить манипулирование объектом Query Builder для других классов.

$spec = new AndX(
    new Equals('ended', 0),
    new OrX(
        new LowerThan('endDate', new \DateTime()),
        new AndX(
            new IsNull('endDate'),
            new LowerThan('startDate', new \DateTime('-4weeks'))
        )
    )
);

return $this->em->getRepository('Advertisement')->match($spec)->execute()

Кроме того, вы можете составить 2 или более классов вместе, что создает хорошие многократно используемые строительные блоки:

public function myQuery(User $user)
{
    $spec = new AndX(
        new ExpiredAds(),
        new AdsByUser($user)
    );

    return $this->em->getRepository('Advertisement')->match($spec)->execute();
}

В этом случае ExpiredAds () и AdsByUser () содержат структуру, как в первом примере кода.

Если вы считаете, что решение подойдет вам, позвольте мне предложить две библиотеки, которые вы можете установить через composer:

...