2016-09-14 2 views
4

Я использую Doctrine 2.4 и у меня есть абстрактный базовый класс, который содержит метод, как следует:Doctrine2 QueryBuilder, что явно не соответствует ничего

protected function getBaseQueryBuilder($type) { 
    switch ($type) { 
     case self::TYPE_1; 
      return $this->em->createQueryBuilder()->...lots of clauses...; 
     case self::TYPE_2; 
      return $this->em->createQueryBuilder()->...lots of clauses...; 
     /* many more types... */ 
     case self::TYPE_N; 
      return /* want to return a query builder for the empty set */ 
    } 
} 

Есть несколько суб-классы, наследуемые от этого базового класса, и каждый из них называет этот метод из нескольких мест. Затем они расширяют возвращаемые серверы запросов с дополнительными классовыми предложениями перед их выполнением. Однако в случае TYPE_N никакие строки не должны совпадать.

Одним из решений является, конечно, возврат null для $type == TYPE_N и проверка каждого звонящего на null, а затем ничего не выполнить. Но было бы гораздо приятнее, если бы я смог вернуть построитель запросов, который никогда не будет соответствовать чему-либо, и для которого запрос никогда не попадет в БД. Это упростило бы многие сайты вызывающих абонентов.

Есть ли способ сделать это?

ответ

3

Вместо того, чтобы возвращать нуль и проверять на это, вы можете создать TypeNQueryBuilder, который расширяет построитель запросов, поэтому он поддерживает интерфейс, но возвращает пользовательский TypeNQuery, который имеет нулевые или пустые результаты, которые вам нужны.

Acme \ Doctrine \ TypeNQuery

use Doctrine\ORM\AbstractQuery; 

class TypeNQuery extends AbstractQuery 
{ 
    /** 
    * Override __construct so it doesn't require EntityManager 
    */ 
    public function __construct() 
    { 

    } 

    /** 
    * {@inheritdoc} 
    */ 
    public function getResult() 
    { 
     return []; 
    } 

    /** 
    * {@inheritdoc} 
    */ 
    public function getOneOrNullResult($hydrationMode = null) 
    { 
     return null; 
    } 

    /** 
    * {@inheritdoc} 
    */ 
    public function getSingleScalarResult() 
    { 
     return 0; 
    } 

    //.. add as necessary 
    // getArrayResult() 
    // getScalarResult() 
    // getSingleResult() 
} 

Acme \ Doctrine \ TypeNQueryBuilder

user Doctrine\ORM\QueryBuilder; 

class TypeNQueryBuilder extends QueryBuilder 
{ 
    /** 
    * Override getQuery() so it returns your TypeNQuery 
    */ 
    public function getQuery() 
    { 
     return new TypeNQuery(); 
    } 
} 

Затем в getBaseQueryBuilder вызова вы можете добавить пункты или вернуть ваши TypeNQueryBuilder в зависимости от типа предоставлена.

protected function getBaseQueryBuilder($type) 
{ 
    $queryBuilder = $this->em->createQueryBuilder(); 

    switch ($type) { 
     case self::TYPE_1: 
      $queryBuilder 
       ->yadaYadaYada(....) 
      ; 
      break; 
     case self::TYPE_2: 
      $queryBuilder 
       ->yadaYadaYada(....) 
      ; 
      break; 
     case self::TYPE_N: 
      return new TypeNQueryBuilder($this->em); 
    } 

    return $queryBuilder; 
} 

С интерфейсом быть тем же самым вы бы просто быть в состоянии использовать ..

$this 
    ->getBaseQueryBuilder($type) 
    ->andWhere(...) 
    ->addOrderBy(...) 
    ->getQuery() 
    ->getOneOrNullResult(); 

.. и в зависимости от типа данного он будет либо построить запрос правильно или просто выпадать на в последнюю минуту и ​​верните результат null.

+0

Мне нравится это решение. Он может быть даже расширен, чтобы переопределить 'andWhere()', 'orderBy()' и т. Д., Чтобы сделать эти операции еще более дешевыми. Я попытаюсь реализовать это и посмотреть, как это происходит. – jlh

+1

Я думаю, что большинство методов типа 'andWhere',' innerJoin' и т. Д. Все что-то делают, а затем вызывают 'add', поэтому вы можете просто переопределить это, чтобы просто вернуть' $ this' и вырезать большую часть работы. – qooplmao

2

Почему вы хотите сделать что-то подобное? Я бы сказал, вы не должны создавать дорогостоящую QueryBuilder объекта, только так вы можете получить null от него позже, когда вы звоните getResult() ...

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

protected function getBaseQueryBuilder($type) { 
    switch ($type) { 
     case self::TYPE_1; 
      return $this->em->createQueryBuilder()->...lots of clauses...; 
     case self::TYPE_2; 
      return $this->em->createQueryBuilder()->...lots of clauses...; 
     case self::TYPE_N; 
      return null; 
    } 
} 

protected function getResultFromBaseQueryBuilder() { 
    $type = $this->getType(); // get your type 
    $queryBuilder = $this->getBaseQueryBuilder($type); 
    if($queryBuilder === null){ 
     return /* empty result set so for example null, [] or new ArrayCollection(); */ 
    } 
    return $queryBuilder->getResult(); 
} 

Вы можете альтернативно проверить тип непосредственно внутри этого метода:

protected function getBaseQueryBuilder($type) { 
    switch ($type) { 
     case self::TYPE_1; 
      return $this->em->createQueryBuilder()->...lots of clauses...; 
     case self::TYPE_2; 
      return $this->em->createQueryBuilder()->...lots of clauses...; 
    } 
} 

protected function getResultFromBaseQueryBuilder() { 
    $type = $this->getType(); // get your type 
    if($type === self:TYPE_N){ 
     return /* empty result set so for example null, [] or new ArrayCollection(); */ 
    } 
    $queryBuilder = $this->getBaseQueryBuilder($type); 
    return $queryBuilder->getResult(); 
} 

Другими словами вместо того, чтобы общий интерфейс, содержащий метод getBaseQueryBuilder, а сделать интерфейс, который содержит метод, который возвращает результат. (Я предлагаю вам скорее позвонить по методу getResult вместо getResultFromBaseQueryBuilder, который я использовал только для пояснения примера).

+0

Я понимаю, что строительство дорогостоящего объекта, чтобы ничего не вернуть от него в конце, является расточительным. Тем не менее, мне кажется, что ваше решение не учитывает то, что я вам назвал: несколько подклассов получат такой построитель запросов, а затем добавят к нему дополнительные предложения до его запуска. Я не вижу, как это возможно с вашим решением, поскольку построитель запросов создается и его запрос выполняется немедленно. Куда будут входить подклассы? – jlh

+0

Основываясь на небольшой информации, которую вы даете мне в своем комментарии, сложно дать вам совет. Например, вы можете сделать метод 'addClause' или' addExpression' для вашей службы, но я чувствую, что могут быть лучшие способы достижения вашей цели. Может быть, вы можете задать другой вопрос, где вы добавите больше подробностей, чтобы объяснить ваш случай пользователя? Вы также можете войти в подкласс, проверьте, действительно ли вы получаете экземпляр 'QueryBuilder', и просто игнорируете случаи, когда у вас есть' TYPE_N' (поэтому нет 'Querybuilder', не имеет смысла добавлять предложения к запросу, никогда не будет исполнена). – Wilt

+0

Вариант использования, который мне требуется, уже описан в исходном вопросе. Так можете ли вы уточнить, какая информация отсутствует? И да, я мог бы сделать чек в подклассах, как я уже упоминал в исходном вопросе. Вопрос конкретно в том, что этого не делать, поскольку для написания кода было бы намного больше. – jlh