1
0
mirror of synced 2025-02-20 14:13:15 +03:00

Add QueryBuilder::addCriteria() for Criteria - QueryBuilder bridge

This commit is contained in:
Kirill chEbba Chebunin 2012-08-19 21:58:04 +04:00
parent ece6a005bc
commit c6b3899c2d
4 changed files with 385 additions and 1 deletions

View 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();
}
}

View File

@ -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.
*

View 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')));
}
}

View File

@ -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()