diff --git a/lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php index a65c73328..e44be6c57 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php @@ -96,7 +96,8 @@ class CountOutputWalker extends SqlWalker throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); } - $rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable; + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; $rootClass = $this->queryComponents[$rootAlias]['metadata']; $rootIdentifier = $rootClass->identifier; diff --git a/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php b/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php index 778dd3072..ff0c6b363 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php @@ -50,31 +50,26 @@ class CountWalker extends TreeWalkerAdapter throw new \RuntimeException('Cannot count query that uses a HAVING clause. Use the output walkers for pagination'); } - $rootComponents = array(); - foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { - $isParent = array_key_exists('parent', $qComp) - && $qComp['parent'] === null - && $qComp['nestingLevel'] == 0 - ; - if ($isParent) { - $rootComponents[] = array($dqlAlias => $qComp); - } - } - if (count($rootComponents) > 1) { + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + + if (count($from) > 1) { throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); } - $root = reset($rootComponents); - $parentName = key($root); - $parent = current($root); - $identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName(); + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); $pathType = PathExpression::TYPE_STATE_FIELD; - if (isset($parent['metadata']->associationMappings[$identifierFieldName])) { + if (isset($rootClass->associationMappings[$identifierFieldName])) { $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; } $pathExpression = new PathExpression( - PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, + PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName ); $pathExpression->type = $pathType; diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php index 1b32fc97c..45a6ce78b 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -122,7 +122,8 @@ class LimitSubqueryOutputWalker extends SqlWalker throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); } - $rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable; + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; $rootClass = $this->queryComponents[$rootAlias]['metadata']; $rootIdentifier = $rootClass->identifier; diff --git a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php index 2cf5e527f..c88191153 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php @@ -60,37 +60,36 @@ class LimitSubqueryWalker extends TreeWalkerAdapter */ public function walkSelectStatement(SelectStatement $AST) { - $parent = null; - $parentName = null; + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; $selectExpressions = array(); - foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { + foreach ($queryComponents as $dqlAlias => $qComp) { // Preserve mixed data in query for ordering. if (isset($qComp['resultVariable'])) { $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias); continue; } - - if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) { - $parent = $qComp; - $parentName = $dqlAlias; - continue; - } } - - $identifier = $parent['metadata']->getSingleIdentifierFieldName(); - if (isset($parent['metadata']->associationMappings[$identifier])) { + + $identifier = $rootClass->getSingleIdentifierFieldName(); + + if (isset($rootClass->associationMappings[$identifier])) { throw new \RuntimeException("Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator."); } $this->_getQuery()->setHint( self::IDENTIFIER_TYPE, - Type::getType($parent['metadata']->getTypeOfField($identifier)) + Type::getType($rootClass->getTypeOfField($identifier)) ); $pathExpression = new PathExpression( PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, - $parentName, + $rootAlias, $identifier ); $pathExpression->type = PathExpression::TYPE_STATE_FIELD; diff --git a/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php b/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php index 27cbc3c2e..9f42a1eea 100644 --- a/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php +++ b/lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php @@ -71,30 +71,25 @@ class WhereInWalker extends TreeWalkerAdapter */ public function walkSelectStatement(SelectStatement $AST) { - $rootComponents = array(); - foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) { - $isParent = array_key_exists('parent', $qComp) - && $qComp['parent'] === null - && $qComp['nestingLevel'] == 0 - ; - if ($isParent) { - $rootComponents[] = array($dqlAlias => $qComp); - } - } - if (count($rootComponents) > 1) { + $queryComponents = $this->_getQueryComponents(); + // Get the root entity and alias from the AST fromClause + $from = $AST->fromClause->identificationVariableDeclarations; + + if (count($from) > 1) { throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction"); } - $root = reset($rootComponents); - $parentName = key($root); - $parent = current($root); - $identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName(); + + $fromRoot = reset($from); + $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable; + $rootClass = $queryComponents[$rootAlias]['metadata']; + $identifierFieldName = $rootClass->getSingleIdentifierFieldName(); $pathType = PathExpression::TYPE_STATE_FIELD; - if (isset($parent['metadata']->associationMappings[$identifierFieldName])) { + if (isset($rootClass->associationMappings[$identifierFieldName])) { $pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION; } - $pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, $identifierFieldName); + $pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName); $pathExpression->type = $pathType; $count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT); diff --git a/tests/Doctrine/Tests/ORM/Tools/Pagination/CountWalkerTest.php b/tests/Doctrine/Tests/ORM/Tools/Pagination/CountWalkerTest.php index 4ef4d1630..e44248926 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Pagination/CountWalkerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Pagination/CountWalkerTest.php @@ -90,5 +90,21 @@ class CountWalkerTest extends PaginationTestCase $query->getSql(); } + + /** + * Arbitrary Join + */ + public function testCountQueryWithArbitraryJoin() + { + $query = $this->entityManager->createQuery( + 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p LEFT JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c'); + $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker')); + $query->setHint(CountWalker::HINT_DISTINCT, true); + $query->setFirstResult(null)->setMaxResults(null); + + $this->assertEquals( + "SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ LEFT JOIN Category c1_ ON (b0_.category_id = c1_.id)", $query->getSql() + ); + } } diff --git a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php index 1bb2b6463..597ec1350 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Pagination/LimitSubqueryWalkerTest.php @@ -67,5 +67,36 @@ class LimitSubqueryWalkerTest extends PaginationTestCase $limitQuery->getSql() ); } + + /** + * Arbitrary Join + */ + public function testLimitSubqueryWithArbitraryJoin() + { + $dql = 'SELECT p, c FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c'; + $query = $this->entityManager->createQuery($dql); + $limitQuery = clone $query; + + $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker')); + + $this->assertEquals( + "SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id)", + $limitQuery->getSql() + ); + } + + public function testLimitSubqueryWithSortWithArbitraryJoin() + { + $dql = 'SELECT p, c FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c ORDER BY p.title'; + $query = $this->entityManager->createQuery($dql); + $limitQuery = clone $query; + + $limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker')); + + $this->assertEquals( + "SELECT DISTINCT m0_.id AS id_0, m0_.title AS title_1 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id) ORDER BY m0_.title ASC", + $limitQuery->getSql() + ); + } } diff --git a/tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php b/tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php index 7291c2786..41152dc5d 100644 --- a/tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php +++ b/tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php @@ -121,5 +121,34 @@ class WhereInWalkerTest extends PaginationTestCase "SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (NOT 1 = 2) AND u0_.id IN (?)", $whereInQuery->getSql() ); } + + /** + * Arbitrary Join + */ + public function testWhereInQueryWithArbitraryJoin_NoWhere() + { + $whereInQuery = $this->entityManager->createQuery( + 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c' + ); + $whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker')); + $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10); + + $this->assertEquals( + "SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ INNER JOIN Category c1_ ON (b0_.category_id = c1_.id) WHERE b0_.id IN (?)", $whereInQuery->getSql() + ); + } + + public function testWhereInQueryWithArbitraryJoin_SingleWhere() + { + $whereInQuery = $this->entityManager->createQuery( + 'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c WHERE 1 = 1' + ); + $whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker')); + $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10); + + $this->assertEquals( + "SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ INNER JOIN Category c1_ ON (b0_.category_id = c1_.id) WHERE 1 = 1 AND b0_.id IN (?)", $whereInQuery->getSql() + ); + } }