Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/DoctrineOrmFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}

Expand Down
34 changes: 26 additions & 8 deletions src/DqlConditionGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand All @@ -41,10 +44,10 @@
*
* @final
*/
final class DqlConditionGenerator extends AbstractConditionGenerator
class DqlConditionGenerator extends AbstractConditionGenerator
{
/**
* @var DqlQuery
* @var DqlQuery|QueryBuilder
*/
private $query;

Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -167,7 +185,7 @@ public function getParameters(): array
/**
* @internal
*/
public function getQuery(): DqlQuery
public function getQuery()
{
return $this->query;
}
Expand Down
31 changes: 30 additions & 1 deletion tests/DqlConditionGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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');
Expand Down Expand Up @@ -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();
Expand Down
123 changes: 123 additions & 0 deletions tests/QueryBuilderWithHints.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

declare(strict_types=1);

/*
* This file is part of the RollerworksSearch package.
*
* (c) Sebastiaan Stok <[email protected]>
*
* 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.
*
* <code>
* $qb = $em->createQueryBuilder()
* ->select('u')
* ->from('User', 'u');
* $q = $qb->getQuery();
* $results = $q->execute();
* </code>
*
* @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;
}
}