diff --git a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php index da4577132..fe547081f 100644 --- a/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php +++ b/lib/Doctrine/ORM/Query/QueryExpressionVisitor.php @@ -45,9 +45,9 @@ class QueryExpressionVisitor extends ExpressionVisitor ); /** - * @var string + * @var array */ - private $rootAlias; + private $queryAliases; /** * @var Expr @@ -62,11 +62,11 @@ class QueryExpressionVisitor extends ExpressionVisitor /** * Constructor * - * @param string $rootAlias + * @param array $queryAliases */ - public function __construct($rootAlias) + public function __construct($queryAliases) { - $this->rootAlias = $rootAlias; + $this->queryAliases = $queryAliases; $this->expr = new Expr(); } @@ -131,6 +131,20 @@ class QueryExpressionVisitor extends ExpressionVisitor */ public function walkComparison(Comparison $comparison) { + + if ( ! isset($this->queryAliases[0])) { + throw new QueryException('No aliases are set before invoking walkComparison().'); + } + + $field = $this->queryAliases[0] . '.' . $comparison->getField(); + + foreach($this->queryAliases as $alias) { + if(strpos($comparison->getField() . '.', $alias . '.') === 0) { + $field = $comparison->getField(); + break; + } + } + $parameterName = str_replace('.', '_', $comparison->getField()); foreach($this->parameters as $parameter) { @@ -146,38 +160,38 @@ class QueryExpressionVisitor extends ExpressionVisitor switch ($comparison->getOperator()) { case Comparison::IN: $this->parameters[] = $parameter; - return $this->expr->in($this->rootAlias . '.' . $comparison->getField(), $placeholder); + return $this->expr->in($field, $placeholder); case Comparison::NIN: $this->parameters[] = $parameter; - return $this->expr->notIn($this->rootAlias . '.' . $comparison->getField(), $placeholder); + return $this->expr->notIn($field, $placeholder); case Comparison::EQ: case Comparison::IS: if ($this->walkValue($comparison->getValue()) === null) { - return $this->expr->isNull($this->rootAlias . '.' . $comparison->getField()); + return $this->expr->isNull($field); } $this->parameters[] = $parameter; - return $this->expr->eq($this->rootAlias . '.' . $comparison->getField(), $placeholder); + return $this->expr->eq($field, $placeholder); case Comparison::NEQ: if ($this->walkValue($comparison->getValue()) === null) { - return $this->expr->isNotNull($this->rootAlias . '.' . $comparison->getField()); + return $this->expr->isNotNull($field); } $this->parameters[] = $parameter; - return $this->expr->neq($this->rootAlias . '.' . $comparison->getField(), $placeholder); + return $this->expr->neq($field, $placeholder); case Comparison::CONTAINS: $parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType()); $this->parameters[] = $parameter; - return $this->expr->like($this->rootAlias . '.' . $comparison->getField(), $placeholder); + return $this->expr->like($field, $placeholder); default: $operator = self::convertComparisonOperator($comparison->getOperator()); if ($operator) { $this->parameters[] = $parameter; return new Expr\Comparison( - $this->rootAlias . '.' . $comparison->getField(), + $field, $operator, $placeholder ); diff --git a/lib/Doctrine/ORM/QueryBuilder.php b/lib/Doctrine/ORM/QueryBuilder.php index 0dda54c7a..f3cad510d 100644 --- a/lib/Doctrine/ORM/QueryBuilder.php +++ b/lib/Doctrine/ORM/QueryBuilder.php @@ -459,6 +459,24 @@ class QueryBuilder return $aliases; } + /** + * Gets all the aliases that have been used in the query. + * Including all select root aliases and join aliases + * + * + * $qb = $em->createQueryBuilder() + * ->select('u') + * ->from('User', 'u') + * ->join('u.articles','a'; + * + * $qb->getAllAliases(); // array('u','a') + * + * @return array + */ + private function getAllAliases() { + return array_merge($this->getRootAliases(),array_keys($this->joinRootAliases)); + } + /** * Gets the root entities of the query. This is the entity aliases involved * in the construction of the query. @@ -1216,13 +1234,17 @@ class QueryBuilder * Overrides firstResult and maxResults if they're set. * * @param Criteria $criteria - * * @return QueryBuilder + * @throws Query\QueryException */ public function addCriteria(Criteria $criteria) { - $rootAlias = $this->getRootAlias(); - $visitor = new QueryExpressionVisitor($rootAlias); + $allAliases = $this->getAllAliases(); + if ( ! isset($allAliases[0])) { + throw new Query\QueryException('No aliases are set before invoking addCriteria().'); + } + + $visitor = new QueryExpressionVisitor($this->getAllAliases()); if ($whereExpression = $criteria->getWhereExpression()) { $this->andWhere($visitor->dispatch($whereExpression)); @@ -1233,7 +1255,20 @@ class QueryBuilder if ($criteria->getOrderings()) { foreach ($criteria->getOrderings() as $sort => $order) { - $this->addOrderBy($rootAlias . '.' . $sort, $order); + + $hasValidAlias = false; + foreach($allAliases as $alias) { + if(strpos($sort . '.', $alias . '.') === 0) { + $hasValidAlias = true; + break; + } + } + + if(!$hasValidAlias) { + $sort = $allAliases[0] . '.' . $sort; + } + + $this->addOrderBy($sort, $order); } } diff --git a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php index 1761da043..2b9fc2304 100644 --- a/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php +++ b/tests/Doctrine/Tests/ORM/Query/QueryExpressionVisitorTest.php @@ -47,7 +47,7 @@ class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase */ protected function setUp() { - $this->visitor = new QueryExpressionVisitor('o'); + $this->visitor = new QueryExpressionVisitor(array('o','p')); } /** @@ -89,6 +89,10 @@ class QueryExpressionVisitorTest extends \PHPUnit_Framework_TestCase // Test parameter conversion array($cb->eq('object.field', 'value'), $qb->eq('o.object.field', ':object_field'), new Parameter('object_field', 'value')), + + // Test alternative rootAlias + array($cb->eq('p.field', 'value'), $qb->eq('p.field', ':p_field'), new Parameter('p_field', 'value')), + array($cb->eq('p.object.field', 'value'), $qb->eq('p.object.field', ':p_object_field'), new Parameter('p_object_field', 'value')), ); } diff --git a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php index fe545927d..c10177942 100644 --- a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php @@ -522,6 +522,25 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('u.field DESC', (string) $orderBy[0]); } + /** + * @group DDC-3108 + */ + public function testAddCriteriaOrderOnJoinAlias() + { + $qb = $this->_em->createQueryBuilder(); + $qb->select('u') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->join('u.article','a'); + + $criteria = new Criteria(); + $criteria->orderBy(array('a.field' => Criteria::DESC)); + + $qb->addCriteria($criteria); + + $this->assertCount(1, $orderBy = $qb->getDQLPart('orderBy')); + $this->assertEquals('a.field DESC', (string) $orderBy[0]); + } + public function testAddCriteriaLimit() { $qb = $this->_em->createQueryBuilder(); @@ -847,6 +866,69 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals(2, $expr->count(), "Modifying the second query should affect the first one."); } + /** + * @group DDC-3108 + */ + public function testAddCriteriaWhereWithJoinAlias() + { + $qb = $this->_em->createQueryBuilder(); + $qb->select('alias1')->from('Doctrine\Tests\Models\CMS\CmsUser', 'alias1'); + $qb->join('alias1.articles','alias2'); + + $criteria = new Criteria(); + $criteria->where($criteria->expr()->eq('field', 'value1')); + $criteria->andWhere($criteria->expr()->gt('alias2.field', 'value2')); + + $qb->addCriteria($criteria); + + $this->assertEquals('alias1.field = :field AND alias2.field > :alias2_field', (string) $qb->getDQLPart('where')); + $this->assertSame('value1', $qb->getParameter('field')->getValue()); + $this->assertSame('value2', $qb->getParameter('alias2_field')->getValue()); + } + + /** + * @group DDC-3108 + */ + public function testAddCriteriaWhereWithDefaultAndJoinAlias() + { + $qb = $this->_em->createQueryBuilder(); + $qb->select('alias1')->from('Doctrine\Tests\Models\CMS\CmsUser', 'alias1'); + $qb->join('alias1.articles','alias2'); + + $criteria = new Criteria(); + $criteria->where($criteria->expr()->eq('alias1.field', 'value1')); + $criteria->andWhere($criteria->expr()->gt('alias2.field', 'value2')); + + $qb->addCriteria($criteria); + + $this->assertEquals('alias1.field = :alias1_field AND alias2.field > :alias2_field', (string) $qb->getDQLPart('where')); + $this->assertSame('value1', $qb->getParameter('alias1_field')->getValue()); + $this->assertSame('value2', $qb->getParameter('alias2_field')->getValue()); + } + + /** + * @group DDC-3108 + */ + public function testAddCriteriaWhereOnJoinAliasWithDuplicateFields() + { + $qb = $this->_em->createQueryBuilder(); + $qb->select('alias1')->from('Doctrine\Tests\Models\CMS\CmsUser', 'alias1'); + $qb->join('alias1.articles','alias2'); + + $criteria = new Criteria(); + $criteria->where($criteria->expr()->eq('alias1.field', 'value1')); + $criteria->andWhere($criteria->expr()->gt('alias2.field', 'value2')); + $criteria->andWhere($criteria->expr()->lt('alias2.field', 'value3')); + + $qb->addCriteria($criteria); + + $this->assertEquals('(alias1.field = :alias1_field AND alias2.field > :alias2_field) AND alias2.field < :alias2_field_2', (string) $qb->getDQLPart('where')); + $this->assertSame('value1', $qb->getParameter('alias1_field')->getValue()); + $this->assertSame('value2', $qb->getParameter('alias2_field')->getValue()); + $this->assertSame('value3', $qb->getParameter('alias2_field_2')->getValue()); + } + + /** * @group DDC-1933 */