Add QueryBuilder::addCriteria() for Criteria - QueryBuilder bridge
This commit is contained in:
parent
ece6a005bc
commit
c6b3899c2d
172
lib/Doctrine/ORM/Query/QueryExpressionVisitor.php
Normal file
172
lib/Doctrine/ORM/Query/QueryExpressionVisitor.php
Normal file
@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
use Doctrine\Common\Collections\Expr\ExpressionVisitor;
|
||||
use Doctrine\Common\Collections\Expr\Comparison;
|
||||
use Doctrine\Common\Collections\Expr\CompositeExpression;
|
||||
use Doctrine\Common\Collections\Expr\Value;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
|
||||
/**
|
||||
* Convert Collection expressions to Query expressions
|
||||
*
|
||||
* @author Kirill chEbba Chebunin <iam@chebba.org>
|
||||
* @since 2.3
|
||||
*/
|
||||
class QueryExpressionVisitor extends ExpressionVisitor
|
||||
{
|
||||
private static $operatorMap = array(
|
||||
Comparison::GT => Expr\Comparison::GT,
|
||||
Comparison::GTE => Expr\Comparison::GTE,
|
||||
Comparison::LT => Expr\Comparison::LT,
|
||||
Comparison::LTE => Expr\Comparison::LTE
|
||||
);
|
||||
|
||||
private $expr;
|
||||
private $parameters;
|
||||
|
||||
/**
|
||||
* Constructor with internal initialization
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->expr = new Expr();
|
||||
$this->parameters = new ArrayCollection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bound parameters.
|
||||
* Filled after {@link dispach()}.
|
||||
*
|
||||
* @return \Doctrine\Common\Collections\Collection
|
||||
*/
|
||||
public function getParameters()
|
||||
{
|
||||
return $this->parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear parameters
|
||||
*/
|
||||
public function clearParameters()
|
||||
{
|
||||
$this->parameters = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Criteria expression to Query one based on static map.
|
||||
*
|
||||
* @param string $criteriaOperator
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
private static function convertComparisonOperator($criteriaOperator)
|
||||
{
|
||||
return isset(self::$operatorMap[$criteriaOperator]) ? self::$operatorMap[$criteriaOperator] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkCompositeExpression(CompositeExpression $expr)
|
||||
{
|
||||
$expressionList = array();
|
||||
|
||||
foreach ($expr->getExpressionList() as $child) {
|
||||
$expressionList[] = $this->dispatch($child);
|
||||
}
|
||||
|
||||
switch($expr->getType()) {
|
||||
case CompositeExpression::TYPE_AND:
|
||||
return new Expr\Andx($expressionList);
|
||||
|
||||
case CompositeExpression::TYPE_OR:
|
||||
return new Expr\Orx($expressionList);
|
||||
|
||||
default:
|
||||
throw new \RuntimeException("Unknown composite " . $expr->getType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkComparison(Comparison $comparison)
|
||||
{
|
||||
$parameterName = str_replace('.', '_', $comparison->getField());
|
||||
$parameter = new Parameter($parameterName, $this->walkValue($comparison->getValue()));
|
||||
$placeholder = ':' . $parameterName;
|
||||
|
||||
switch ($comparison->getOperator()) {
|
||||
case Comparison::IN:
|
||||
$this->parameters->add($parameter);
|
||||
return $this->expr->in($comparison->getField(), $placeholder);
|
||||
|
||||
case Comparison::NIN:
|
||||
$this->parameters->add($parameter);
|
||||
return $this->expr->notIn($comparison->getField(), $placeholder);
|
||||
|
||||
case Comparison::EQ:
|
||||
case Comparison::IS:
|
||||
if ($this->walkValue($comparison->getValue()) === null) {
|
||||
return $this->expr->isNull($comparison->getField());
|
||||
} else {
|
||||
$this->parameters->add($parameter);
|
||||
return $this->expr->eq($comparison->getField(), $placeholder);
|
||||
}
|
||||
|
||||
case Comparison::NEQ:
|
||||
if ($this->walkValue($comparison->getValue()) === null) {
|
||||
return $this->expr->isNotNull($comparison->getField());
|
||||
} else {
|
||||
$this->parameters->add($parameter);
|
||||
return $this->expr->neq($comparison->getField(), $placeholder);
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
$operator = self::convertComparisonOperator($comparison->getOperator());
|
||||
if ($operator) {
|
||||
$this->parameters->add($parameter);
|
||||
return new Expr\Comparison(
|
||||
$comparison->getField(),
|
||||
$operator,
|
||||
$placeholder
|
||||
);
|
||||
}
|
||||
|
||||
throw new \RuntimeException("Unknown comparison operator: " . $comparison->getOperator());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function walkValue(Value $value)
|
||||
{
|
||||
return $value->getValue();
|
||||
}
|
||||
}
|
@ -20,8 +20,10 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Criteria;
|
||||
|
||||
use Doctrine\ORM\Query\Expr;
|
||||
use Doctrine\ORM\Query\QueryExpressionVisitor;
|
||||
|
||||
/**
|
||||
* This class is responsible for building DQL query strings via an object oriented
|
||||
@ -1018,6 +1020,38 @@ class QueryBuilder
|
||||
return $this->add('orderBy', new Expr\OrderBy($sort, $order), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add criteria to query.
|
||||
* Add where expressions with AND operator.
|
||||
* Add orderings.
|
||||
* Override firstResult and maxResults.
|
||||
*
|
||||
* @param Criteria $criteria
|
||||
* @return QueryBuilder
|
||||
*/
|
||||
public function addCriteria(Criteria $criteria)
|
||||
{
|
||||
$visitor = new QueryExpressionVisitor();
|
||||
|
||||
if ($whereExpression = $criteria->getWhereExpression()) {
|
||||
$this->andWhere($visitor->dispatch($whereExpression));
|
||||
foreach ($visitor->getParameters() as $parameter) {
|
||||
$this->parameters->add($parameter);
|
||||
}
|
||||
}
|
||||
|
||||
if ($criteria->getOrderings()) {
|
||||
foreach ($criteria->getOrderings() as $sort => $order) {
|
||||
$this->addOrderBy($sort, $order);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setFirstResult($criteria->getFirstResult());
|
||||
$this->setMaxResults($criteria->getMaxResults());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a query part by its name.
|
||||
*
|
||||
|
137
tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php
Normal file
137
tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Tests\ORM\Query;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
use Doctrine\Common\Collections\Expr\Value;
|
||||
use Doctrine\Common\Collections\Expr\Comparison as CriteriaComparison;
|
||||
use Doctrine\ORM\Query\Expr\Comparison as QueryComparison;
|
||||
use Doctrine\Common\Collections\ExpressionBuilder as CriteriaBuilder;
|
||||
use Doctrine\ORM\Query\Expr as QueryBuilder;
|
||||
|
||||
use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Query\QueryExpressionVisitor;
|
||||
|
||||
/**
|
||||
* Test for QueryExpressionVisitor
|
||||
*
|
||||
* @author Kirill chEbba Chebunin <iam@chebba.org>
|
||||
*/
|
||||
class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var QueryExpressionVisitor
|
||||
*/
|
||||
private $visitor;
|
||||
/**
|
||||
* @var CriteriaBuilder
|
||||
*/
|
||||
private $criteriaBuilder;
|
||||
/**
|
||||
* @var QueryBuilder
|
||||
*/
|
||||
private $queryBuilder;
|
||||
|
||||
public function __construct($name = NULL, array $data = array(), $dataName = '')
|
||||
{
|
||||
$this->criteriaBuilder = new CriteriaBuilder();
|
||||
$this->queryBuilder = new QueryBuilder();
|
||||
|
||||
parent::__construct($name, $data, $dataName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
$this->visitor = new QueryExpressionVisitor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CriteriaComparison $criteriaExpr
|
||||
* @param QueryComparison|string $queryExpr
|
||||
* @param Parameter $parameter
|
||||
*
|
||||
* @dataProvider comparisonData
|
||||
*/
|
||||
public function testWalkComparison(CriteriaComparison $criteriaExpr, $queryExpr, Parameter $parameter = null)
|
||||
{
|
||||
$this->assertEquals($queryExpr, $this->visitor->walkComparison($criteriaExpr));
|
||||
if ($parameter) {
|
||||
$this->assertEquals(new ArrayCollection(array($parameter)), $this->visitor->getParameters());
|
||||
}
|
||||
}
|
||||
|
||||
public function comparisonData()
|
||||
{
|
||||
return array(
|
||||
array($this->criteriaBuilder->eq('field', 'value'), $this->queryBuilder->eq('field', ':field'), new Parameter('field', 'value')),
|
||||
array($this->criteriaBuilder->neq('field', 'value'), $this->queryBuilder->neq('field', ':field'), new Parameter('field', 'value')),
|
||||
array($this->criteriaBuilder->eq('field', null), $this->queryBuilder->isNull('field')),
|
||||
array($this->criteriaBuilder->neq('field', null), $this->queryBuilder->isNotNull('field')),
|
||||
array($this->criteriaBuilder->isNull('field'), $this->queryBuilder->isNull('field')),
|
||||
|
||||
array($this->criteriaBuilder->gt('field', 'value'), $this->queryBuilder->gt('field', ':field'), new Parameter('field', 'value')),
|
||||
array($this->criteriaBuilder->gte('field', 'value'), $this->queryBuilder->gte('field', ':field'), new Parameter('field', 'value')),
|
||||
array($this->criteriaBuilder->lt('field', 'value'), $this->queryBuilder->lt('field', ':field'), new Parameter('field', 'value')),
|
||||
array($this->criteriaBuilder->lte('field', 'value'), $this->queryBuilder->lte('field', ':field'), new Parameter('field', 'value')),
|
||||
|
||||
array($this->criteriaBuilder->in('field', array('value')), $this->queryBuilder->in('field', ':field'), new Parameter('field', array('value'))),
|
||||
array($this->criteriaBuilder->notIn('field', array('value')), $this->queryBuilder->notIn('field', ':field'), new Parameter('field', array('value'))),
|
||||
|
||||
// Test parameter conversion
|
||||
array($this->criteriaBuilder->eq('object.field', 'value'), $this->queryBuilder->eq('object.field', ':object_field'), new Parameter('object_field', 'value')),
|
||||
);
|
||||
}
|
||||
|
||||
public function testWalkAndCompositeExpression()
|
||||
{
|
||||
$expr = $this->visitor->walkCompositeExpression(
|
||||
$this->criteriaBuilder->andX(
|
||||
$this->criteriaBuilder->eq("foo", 1),
|
||||
$this->criteriaBuilder->eq("bar", 1)
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\ORM\Query\Expr\Andx', $expr);
|
||||
$this->assertCount(2, $expr->getParts());
|
||||
}
|
||||
|
||||
public function testWalkOrCompositeExpression()
|
||||
{
|
||||
$expr = $this->visitor->walkCompositeExpression(
|
||||
$this->criteriaBuilder->orX(
|
||||
$this->criteriaBuilder->eq("foo", 1),
|
||||
$this->criteriaBuilder->eq("bar", 1)
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertInstanceOf('Doctrine\ORM\Query\Expr\Orx', $expr);
|
||||
$this->assertCount(2, $expr->getParts());
|
||||
}
|
||||
|
||||
public function testWalkValue()
|
||||
{
|
||||
$this->assertEquals('value', $this->visitor->walkValue(new Value('value')));
|
||||
}
|
||||
}
|
@ -19,7 +19,8 @@
|
||||
|
||||
namespace Doctrine\Tests\ORM;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\ArrayCollection,
|
||||
Doctrine\Common\Collections\Criteria;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder,
|
||||
Doctrine\ORM\Query\Expr,
|
||||
@ -38,6 +39,9 @@ require_once __DIR__ . '/../TestInit.php';
|
||||
*/
|
||||
class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
|
||||
{
|
||||
/**
|
||||
* @var \Doctrine\ORM\EntityManager
|
||||
*/
|
||||
private $_em;
|
||||
|
||||
protected function setUp()
|
||||
@ -371,6 +375,43 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertValidQueryBuilder($qb, 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u ORDER BY u.username ASC, u.username DESC');
|
||||
}
|
||||
|
||||
public function testAddCriteriaWhere()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$criteria = new Criteria();
|
||||
$criteria->where($criteria->expr()->eq('field', 'value'));
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
$this->assertEquals('field = :field', (string) $qb->getDQLPart('where'));
|
||||
$this->assertNotNull($qb->getParameter('field'));
|
||||
}
|
||||
|
||||
public function testAddCriteriaOrder()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$criteria = new Criteria();
|
||||
$criteria->orderBy(array('field' => Criteria::DESC));
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
$this->assertCount(1, $orderBy = $qb->getDQLPart('orderBy'));
|
||||
$this->assertEquals('field DESC', (string) $orderBy[0]);
|
||||
}
|
||||
|
||||
public function testAddCriteriaLimit()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder();
|
||||
$criteria = new Criteria();
|
||||
$criteria->setFirstResult(2);
|
||||
$criteria->setMaxResults(10);
|
||||
|
||||
$qb->addCriteria($criteria);
|
||||
|
||||
$this->assertEquals(2, $qb->getFirstResult());
|
||||
$this->assertEquals(10, $qb->getMaxResults());
|
||||
}
|
||||
|
||||
public function testGetQuery()
|
||||
{
|
||||
$qb = $this->_em->createQueryBuilder()
|
||||
|
Loading…
x
Reference in New Issue
Block a user