composer package updates
[openemr.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Tools / Pagination / CountOutputWalker.php
blobec9083ad55a738688fe13750d6a950c97e549efb
1 <?php
2 /**
3 * Doctrine ORM
5 * LICENSE
7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * If you did not receive a copy of the license and are unable to
10 * obtain it through the world-wide-web, please send an email
11 * to kontakt@beberlei.de so I can send you a copy immediately.
14 namespace Doctrine\ORM\Tools\Pagination;
16 use Doctrine\ORM\Query\SqlWalker;
17 use Doctrine\ORM\Query\AST\SelectStatement;
19 /**
20 * Wraps the query in order to accurately count the root objects.
22 * Given a DQL like `SELECT u FROM User u` it will generate an SQL query like:
23 * SELECT COUNT(*) (SELECT DISTINCT <id> FROM (<original SQL>))
25 * Works with composite keys but cannot deal with queries that have multiple
26 * root entities (e.g. `SELECT f, b from Foo, Bar`)
28 * @author Sander Marechal <s.marechal@jejik.com>
30 class CountOutputWalker extends SqlWalker
32 /**
33 * @var \Doctrine\DBAL\Platforms\AbstractPlatform
35 private $platform;
37 /**
38 * @var \Doctrine\ORM\Query\ResultSetMapping
40 private $rsm;
42 /**
43 * @var array
45 private $queryComponents;
47 /**
48 * Constructor.
50 * Stores various parameters that are otherwise unavailable
51 * because Doctrine\ORM\Query\SqlWalker keeps everything private without
52 * accessors.
54 * @param \Doctrine\ORM\Query $query
55 * @param \Doctrine\ORM\Query\ParserResult $parserResult
56 * @param array $queryComponents
58 public function __construct($query, $parserResult, array $queryComponents)
60 $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();
61 $this->rsm = $parserResult->getResultSetMapping();
62 $this->queryComponents = $queryComponents;
64 parent::__construct($query, $parserResult, $queryComponents);
67 /**
68 * Walks down a SelectStatement AST node, wrapping it in a COUNT (SELECT DISTINCT).
70 * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL)
71 * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery
72 * that will most likely be executed next can be read from the native SQL cache.
74 * @param SelectStatement $AST
76 * @return string
78 * @throws \RuntimeException
80 public function walkSelectStatement(SelectStatement $AST)
82 if ($this->platform->getName() === "mssql") {
83 $AST->orderByClause = null;
86 $sql = parent::walkSelectStatement($AST);
88 if ($AST->groupByClause) {
89 return sprintf(
90 'SELECT %s AS dctrn_count FROM (%s) dctrn_table',
91 $this->platform->getCountExpression('*'),
92 $sql
96 // Find out the SQL alias of the identifier column of the root entity
97 // It may be possible to make this work with multiple root entities but that
98 // would probably require issuing multiple queries or doing a UNION SELECT
99 // so for now, It's not supported.
101 // Get the root entity and alias from the AST fromClause
102 $from = $AST->fromClause->identificationVariableDeclarations;
103 if (count($from) > 1) {
104 throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
107 $fromRoot = reset($from);
108 $rootAlias = $fromRoot->rangeVariableDeclaration->aliasIdentificationVariable;
109 $rootClass = $this->queryComponents[$rootAlias]['metadata'];
110 $rootIdentifier = $rootClass->identifier;
112 // For every identifier, find out the SQL alias by combing through the ResultSetMapping
113 $sqlIdentifier = array();
114 foreach ($rootIdentifier as $property) {
115 if (isset($rootClass->fieldMappings[$property])) {
116 foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) {
117 if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) {
118 $sqlIdentifier[$property] = $alias;
123 if (isset($rootClass->associationMappings[$property])) {
124 $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name'];
126 foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) {
127 if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) {
128 $sqlIdentifier[$property] = $alias;
134 if (count($rootIdentifier) != count($sqlIdentifier)) {
135 throw new \RuntimeException(sprintf(
136 'Not all identifier properties can be found in the ResultSetMapping: %s',
137 implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier)))
141 // Build the counter query
142 return sprintf('SELECT %s AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table',
143 $this->platform->getCountExpression('*'),
144 implode(', ', $sqlIdentifier),
145 $sql