Расширенная фильтрация коллекции связанных лиц в Symfony

Если у меня есть связанный объект, который является коллекцией, какие параметры у вас есть при извлечении?

например, допустим, что у меня есть объект $view с этим определением внутри него:

 /** * @ORM\OneToMany(targetEntity="\Gutensite\CmsBundle\Entity\View\ViewVersion", mappedBy="entity") * @ORM\OrderBy({"timeMod" = "DESC"}) */ protected $versions; public function getVersions() { return $this->versions; } 

И я хочу получить все версии, связанные с сущностью, как это:

 $view->getVersions(); 

Это вернет коллекцию. Отлично. Но можно ли взять эту коллекцию и отфильтровать ее по критериям, например, более поздней, чем определенная дата? Или заказать его по некоторым (другим) критериям?

Или на данный момент вы просто ожидаете сделать запрос в репозитории:

 $versions = $em->getRepository("GutensiteCmsBundle:View\ViewVersion")->findBy(array( array( 'viewId', $view->getId(), 'timeMod', time()-3600 ) // order by array('timeMod', 'DESC') )); 

В последних версиях Doctrine есть удивительно неизвестная особенность, которая делает эти запросы намного проще.

Кажется, у него нет имени, но вы можете прочитать об этом в документах Doctrine в разделе 9.8 «Фильтрация коллекций» .

В коллекциях есть API фильтрации, который позволяет обрезать части данных из коллекции. Если коллекция еще не загружена из базы данных, API-интерфейс фильтрации может работать на уровне SQL, чтобы обеспечить оптимизированный доступ к большим коллекциям.

В вашем случае вы можете написать такой метод, как на объекте View .

 use Doctrine\Common\Collections\Criteria; class View { // ... public function getVersionsNewerThan(\DateTime $time) { $newerCriteria = Criteria::create() ->where(Criteria::expr()->gt("timeMod", $time)); return $this->getVersions()->matching($newerCriteria); } } 

Это сделает одну из двух вещей:

  1. Если коллекция гидратирована, она будет использовать PHP для фильтрации существующей коллекции.
  2. Если коллекция не гидратирована, она будет получать частичную коллекцию из базы данных с использованием ограничений SQL.

Это действительно здорово, потому что подключение методов репозитория к вашим представлениям обычно беспорядочно и подвержено перерыву.

Мне также нравится ответ @ igor-pantovic, хотя я видел, как этот метод вызывает некоторые смешные ошибки.

Я бы лично избегал использования порядка по аннотации напрямую. Да, вы должны делать запрос так же, как если бы вы использовали необработанный SQL без Doctrine.

Тем не менее, я бы не сделал этого в тот момент, но даже раньше. В вашем конкретном случае я бы создал класс ViewRepository :

 class ViewRepository extends EntityRepository { public function findWithVersionsNewerThan($id, \DateTime $time) { return $this->createQueryBuilder('view') ->addSelect('version') ->join('view.versions', 'version') ->where('view.id = :id') ->andWhere('version.timeMod > :time') ->setParameter('time', $time) ->setParameter('id', $id) ->getQuery() ->getOneOrNullResult(); } } 

Теперь вы можете сделать:

 $yourDateTime = // Calculate it here ... ; $view = $em->getRepository("GutensiteCmsBundle:View\ViewVersion")->findWithVersionsNewerThan($yourDateTime); $versions = $view->getVersions(); // Will only contain versions newer than datetime provided 

Я пишу код с верхней части моей головы здесь, поэтому очень жаль, если прошита синтаксис или ошибка имени метода.