From 78aa9e07e54ecd889871c693961ffb3731189cf1 Mon Sep 17 00:00:00 2001 From: Sebastiaan Stok Date: Tue, 28 Mar 2017 16:02:41 +0200 Subject: [PATCH] Allow QueryBuilder when hints are supported --- src/DoctrineOrmFactory.php | 5 +- src/DqlConditionGenerator.php | 34 ++++++-- tests/DqlConditionGeneratorTest.php | 31 ++++++- tests/QueryBuilderWithHints.php | 123 ++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 tests/QueryBuilderWithHints.php diff --git a/src/DoctrineOrmFactory.php b/src/DoctrineOrmFactory.php index 727754e..f2b0747 100644 --- a/src/DoctrineOrmFactory.php +++ b/src/DoctrineOrmFactory.php @@ -15,6 +15,7 @@ use Doctrine\ORM\NativeQuery; use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; use Psr\SimpleCache\CacheInterface as Cache; use Rollerworks\Component\Search\SearchCondition; @@ -54,7 +55,9 @@ public function createConditionGenerator($query, SearchCondition $searchConditio { if ($query instanceof NativeQuery) { return new NativeQueryConditionGenerator($query, $searchCondition); - } elseif ($query instanceof Query) { + } + + if ($query instanceof Query || $query instanceof QueryBuilder) { return new DqlConditionGenerator($query, $searchCondition); } diff --git a/src/DqlConditionGenerator.php b/src/DqlConditionGenerator.php index c3903d7..8fbde24 100644 --- a/src/DqlConditionGenerator.php +++ b/src/DqlConditionGenerator.php @@ -14,10 +14,13 @@ namespace Rollerworks\Component\Search\Doctrine\Orm; use Doctrine\ORM\Query as DqlQuery; +use Doctrine\ORM\QueryBuilder; use Rollerworks\Component\Search\Doctrine\Dbal\Query\QueryGenerator; use Rollerworks\Component\Search\Doctrine\Dbal\QueryPlatform; use Rollerworks\Component\Search\Doctrine\Orm\QueryPlatform\DqlQueryPlatform; use Rollerworks\Component\Search\Exception\BadMethodCallException; +use Rollerworks\Component\Search\Exception\InvalidArgumentException; +use Rollerworks\Component\Search\Exception\UnexpectedTypeException; use Rollerworks\Component\Search\SearchCondition; /** @@ -41,10 +44,10 @@ * * @final */ -final class DqlConditionGenerator extends AbstractConditionGenerator +class DqlConditionGenerator extends AbstractConditionGenerator { /** - * @var DqlQuery + * @var DqlQuery|QueryBuilder */ private $query; @@ -61,11 +64,19 @@ final class DqlConditionGenerator extends AbstractConditionGenerator /** * Constructor. * - * @param DqlQuery $query Doctrine ORM Query object - * @param SearchCondition $searchCondition SearchCondition object + * @param DqlQuery|QueryBuilder $query Doctrine ORM Query object + * @param SearchCondition $searchCondition SearchCondition object */ - public function __construct(DqlQuery $query, SearchCondition $searchCondition) + public function __construct($query, SearchCondition $searchCondition) { + if ($query instanceof QueryBuilder) { + if (!method_exists($query, 'setHint')) { + throw new InvalidArgumentException(sprintf('An "%s" instance was provided but method setHint is not implemented in "%s".', QueryBuilder::class, get_class($query))); + } + } elseif (!$query instanceof DqlQuery) { + throw new UnexpectedTypeException($query, [DqlQuery::class, QueryBuilder::class.' (with QueryHint support)']); + } + parent::__construct($searchCondition, $query->getEntityManager()); $this->query = $query; @@ -118,11 +129,18 @@ public function updateQuery(string $prependQuery = ' WHERE ') { $whereCase = $this->getWhereClause($prependQuery); - if ('' !== $whereCase) { + if ('' === $whereCase) { + return $this; + } + + if ($this->query instanceof QueryBuilder) { + $this->query->andWhere($this->getWhereClause()); + } else { $this->query->setDQL($this->query->getDQL().$whereCase); - $this->query->setHint($this->getQueryHintName(), $this->getQueryHintValue()); } + $this->query->setHint($this->getQueryHintName(), $this->getQueryHintValue()); + return $this; } @@ -167,7 +185,7 @@ public function getParameters(): array /** * @internal */ - public function getQuery(): DqlQuery + public function getQuery() { return $this->query; } diff --git a/tests/DqlConditionGeneratorTest.php b/tests/DqlConditionGeneratorTest.php index a8380c4..83fba6a 100644 --- a/tests/DqlConditionGeneratorTest.php +++ b/tests/DqlConditionGeneratorTest.php @@ -15,10 +15,13 @@ use Doctrine\ORM\Query; use Doctrine\ORM\Query\QueryException; +use Doctrine\ORM\QueryBuilder; +use Prophecy\Argument; use Rollerworks\Component\Search\Doctrine\Dbal\ColumnConversion; use Rollerworks\Component\Search\Doctrine\Dbal\ConversionHints; use Rollerworks\Component\Search\Doctrine\Dbal\ValueConversion; use Rollerworks\Component\Search\Doctrine\Orm\DqlConditionGenerator; +use Rollerworks\Component\Search\Doctrine\Orm\SqlConversionInfo; use Rollerworks\Component\Search\Extension\Core\Type\ChoiceType; use Rollerworks\Component\Search\Extension\Core\Type\DateType; use Rollerworks\Component\Search\Extension\Core\Type\IntegerType; @@ -42,7 +45,7 @@ protected function getFieldSet(bool $build = true) return $build ? $fieldSet->getFieldSet('invoice') : $fieldSet; } - private function getConditionGenerator(SearchCondition $condition, Query $query = null, $noMapping = false) + private function getConditionGenerator(SearchCondition $condition, $query = null, $noMapping = false) { if (null === $query) { $query = $this->em->createQuery('SELECT I FROM Rollerworks\Component\Search\Tests\Doctrine\Orm\Fixtures\Entity\ECommerceInvoice I JOIN I.customer C'); @@ -612,6 +615,32 @@ public function testUpdateQuery() ); } + public function testUpdateQueryWithQueryBuilder() + { + $condition = SearchConditionBuilder::create($this->getFieldSet()) + ->field('customer') + ->addSimpleValue(2) + ->end() + ->getSearchCondition(); + + if (method_exists(QueryBuilder::class, 'setHint')) { + $qb = $this->prophesize(QueryBuilder::class); + } else { + $qb = $this->prophesize(QueryBuilderWithHints::class); + } + + $qb->getEntityManager()->willReturn($this->em); + $qb->setHint('rws_conversion_hint', Argument::type(SqlConversionInfo::class))->shouldBeCalled(); + $qb->andWhere('((C.id IN(2)))')->shouldBeCalled(); + + $conditionGenerator = $this->getConditionGenerator($condition, $qb->reveal()); + + $whereCase = $conditionGenerator->getWhereClause(); + $conditionGenerator->updateQuery(' WHERE '); + + $this->assertEquals('((C.id IN(2)))', $whereCase); + } + public function testUpdateQueryWithNoResult() { $condition = SearchConditionBuilder::create($this->getFieldSet())->getSearchCondition(); diff --git a/tests/QueryBuilderWithHints.php b/tests/QueryBuilderWithHints.php new file mode 100644 index 0000000..36df5fe --- /dev/null +++ b/tests/QueryBuilderWithHints.php @@ -0,0 +1,123 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Rollerworks\Component\Search\Tests\Doctrine\Orm; + +use Doctrine\ORM\Query; +use Doctrine\ORM\QueryBuilder; + +/** @internal */ +class QueryBuilderWithHints extends QueryBuilder +{ + /** + * Sets a query hint. + * + * @param string $name the name of the hint + * @param mixed $value the value of the hint + * + * @return self + */ + public function setHint($name, $value) + { + $this->_hints[$name] = $value; + + return $this; + } + + /** + * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned. + * + * @param string $name the name of the hint + * + * @return mixed the value of the hint or FALSE, if the hint name is not recognized + */ + public function getHint($name) + { + return $this->_hints[$name] ?? false; + } + + /** + * Check if the query has a hint. + * + * @param string $name The name of the hint + * + * @return bool False if the query does not have any hint + */ + public function hasHint($name) + { + return isset($this->_hints[$name]); + } + + /** + * Return the key value map of query hints that are currently set. + * + * @return array + */ + public function getHints() + { + return $this->_hints; + } + + /** + * The map of query hints. + * + * @var array + */ + private $_hints = []; + + /** + * Constructs a Query instance from the current specifications of the builder. + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u'); + * $q = $qb->getQuery(); + * $results = $q->execute(); + * + * + * @return Query + */ + public function getQuery() + { + $parameters = clone $this->getParameters(); + $query = $this->getEntityManager()->createQuery($this->getDQL()) + ->setParameters($parameters) + ->setFirstResult($this->getFirstResult()) + ->setMaxResults($this->getMaxResults()); + + if ($this->lifetime) { + $query->setLifetime($this->lifetime); + } + + if ($this->cacheMode) { + $query->setCacheMode($this->cacheMode); + } + + if ($this->cacheable) { + $query->setCacheable($this->cacheable); + } + + if ($this->cacheRegion) { + $query->setCacheRegion($this->cacheRegion); + } + + if ($this->_hints) { + foreach ($this->_hints as $name => $value) { + $query->setHint($name, $value); + } + } + + return $query; + } +}