8 * This source file is subject to the new BSD license that is bundled
9 * with this package in the file LICENSE. This license can also be viewed
10 * at http://hobodave.com/license.txt
12 * @category DoctrineExtensions
13 * @package DoctrineExtensions\Paginate
14 * @author David Abdemoulaie <dave@hobodave.com>
15 * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
16 * @license http://hobodave.com/license.txt New BSD License
19 namespace Doctrine\ORM\Tools\Pagination;
21 use Doctrine\DBAL\Types\Type,
22 Doctrine\ORM\Query\TreeWalkerAdapter,
23 Doctrine\ORM\Query\AST\SelectStatement,
24 Doctrine\ORM\Query\AST\SelectExpression,
25 Doctrine\ORM\Query\AST\PathExpression,
26 Doctrine\ORM\Query\AST\AggregateExpression;
29 * Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent
31 * @category DoctrineExtensions
32 * @package DoctrineExtensions\Paginate
33 * @author David Abdemoulaie <dave@hobodave.com>
34 * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
35 * @license http://hobodave.com/license.txt New BSD License
37 class LimitSubqueryWalker extends TreeWalkerAdapter
42 const IDENTIFIER_TYPE = 'doctrine_paginator.id.type';
45 * @var int Counter for generating unique order column aliases
47 private $_aliasCounter = 0;
50 * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids
53 * @param SelectStatement $AST
56 public function walkSelectStatement(SelectStatement $AST)
60 $selectExpressions = array();
62 foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {
63 // preserve mixed data in query for ordering
64 if (isset($qComp['resultVariable'])) {
65 $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias);
69 if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
71 $parentName = $dqlAlias;
76 $identifier = $parent['metadata']->getSingleIdentifierFieldName();
77 if (isset($parent['metadata']->associationMappings[$identifier])) {
78 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.");
81 $this->_getQuery()->setHint(
82 self::IDENTIFIER_TYPE,
83 Type::getType($parent['metadata']->getTypeOfField($identifier))
86 $pathExpression = new PathExpression(
87 PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
91 $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
93 array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id'));
94 $AST->selectClause->selectExpressions = $selectExpressions;
96 if (isset($AST->orderByClause)) {
97 foreach ($AST->orderByClause->orderByItems as $item) {
98 if ($item->expression instanceof PathExpression) {
99 $pathExpression = new PathExpression(
100 PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
101 $item->expression->identificationVariable,
102 $item->expression->field
104 $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
105 $AST->selectClause->selectExpressions[] = new SelectExpression(
107 '_dctrn_ord' . $this->_aliasCounter++
113 $AST->selectClause->isDistinct = true;