LimitSubqueryWalker.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. <?php
  2. /**
  3. * Doctrine ORM
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE. This license can also be viewed
  9. * at http://hobodave.com/license.txt
  10. *
  11. * @category DoctrineExtensions
  12. * @package DoctrineExtensions\Paginate
  13. * @author David Abdemoulaie <dave@hobodave.com>
  14. * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
  15. * @license http://hobodave.com/license.txt New BSD License
  16. */
  17. namespace Doctrine\ORM\Tools\Pagination;
  18. use Doctrine\DBAL\Types\Type,
  19. Doctrine\ORM\Query\TreeWalkerAdapter,
  20. Doctrine\ORM\Query\AST\SelectStatement,
  21. Doctrine\ORM\Query\AST\SelectExpression,
  22. Doctrine\ORM\Query\AST\PathExpression,
  23. Doctrine\ORM\Query\AST\AggregateExpression;
  24. /**
  25. * Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent
  26. *
  27. * @category DoctrineExtensions
  28. * @package DoctrineExtensions\Paginate
  29. * @author David Abdemoulaie <dave@hobodave.com>
  30. * @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
  31. * @license http://hobodave.com/license.txt New BSD License
  32. */
  33. class LimitSubqueryWalker extends TreeWalkerAdapter
  34. {
  35. /**
  36. * ID type hint
  37. */
  38. const IDENTIFIER_TYPE = 'doctrine_paginator.id.type';
  39. /**
  40. * @var int Counter for generating unique order column aliases
  41. */
  42. private $_aliasCounter = 0;
  43. /**
  44. * Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids
  45. * of the root Entity
  46. *
  47. * @param SelectStatement $AST
  48. * @return void
  49. */
  50. public function walkSelectStatement(SelectStatement $AST)
  51. {
  52. $parent = null;
  53. $parentName = null;
  54. $selectExpressions = array();
  55. foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
  56. // preserve mixed data in query for ordering
  57. if (isset($qComp['resultVariable'])) {
  58. $selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias);
  59. continue;
  60. }
  61. if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
  62. $parent = $qComp;
  63. $parentName = $dqlAlias;
  64. continue;
  65. }
  66. }
  67. $identifier = $parent['metadata']->getSingleIdentifierFieldName();
  68. $this->_getQuery()->setHint(
  69. self::IDENTIFIER_TYPE,
  70. Type::getType($parent['metadata']->getTypeOfField($identifier))
  71. );
  72. $pathExpression = new PathExpression(
  73. PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
  74. $parentName,
  75. $identifier
  76. );
  77. $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
  78. array_unshift($selectExpressions, new SelectExpression($pathExpression, '_dctrn_id'));
  79. $AST->selectClause->selectExpressions = $selectExpressions;
  80. if (isset($AST->orderByClause)) {
  81. foreach ($AST->orderByClause->orderByItems as $item) {
  82. if ($item->expression instanceof PathExpression) {
  83. $pathExpression = new PathExpression(
  84. PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
  85. $item->expression->identificationVariable,
  86. $item->expression->field
  87. );
  88. $pathExpression->type = PathExpression::TYPE_STATE_FIELD;
  89. $AST->selectClause->selectExpressions[] = new SelectExpression(
  90. $pathExpression,
  91. '_dctrn_ord' . $this->_aliasCounter++
  92. );
  93. }
  94. }
  95. }
  96. $AST->selectClause->isDistinct = true;
  97. }
  98. }