Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Tools / Pagination / CountOutputWalker.php
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.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.
12  */
13
14 namespace Doctrine\ORM\Tools\Pagination;
15
16 use Doctrine\ORM\Query\SqlWalker,
17     Doctrine\ORM\Query\AST\SelectStatement;
18
19 /**
20  * Wrap the query in order to accurately count the root objects
21  *
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>))
24  *
25  * Works with composite keys but cannot deal with queries that have multiple
26  * root entities (e.g. `SELECT f, b from Foo, Bar`)
27  *
28  * @author Sander Marechal <s.marechal@jejik.com>
29  */
30 class CountOutputWalker extends SqlWalker
31 {
32     /**
33      * @var Doctrine\DBAL\Platforms\AbstractPlatform
34      */
35     private $platform;
36
37     /**
38      * @var Doctrine\ORM\Query\ResultSetMapping
39      */
40     private $rsm;
41
42     /**
43      * @var array
44      */
45     private $queryComponents;
46
47     /**
48      * Constructor. Stores various parameters that are otherwise unavailable
49      * because Doctrine\ORM\Query\SqlWalker keeps everything private without
50      * accessors.
51      *
52      * @param Doctrine\ORM\Query $query
53      * @param Doctrine\ORM\Query\ParserResult $parserResult
54      * @param array $queryComponents
55      */
56     public function __construct($query, $parserResult, array $queryComponents)
57     {
58         $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();
59         $this->rsm = $parserResult->getResultSetMapping();
60         $this->queryComponents = $queryComponents;
61
62         parent::__construct($query, $parserResult, $queryComponents);
63     }
64
65     /**
66      * Walks down a SelectStatement AST node, wrapping it in a COUNT (SELECT DISTINCT)
67      *
68      * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL)
69      * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery
70      * that will most likely be executed next can be read from the native SQL cache.
71      *
72      * @param SelectStatement $AST
73      * @return string
74      */
75     public function walkSelectStatement(SelectStatement $AST)
76     {
77         $sql = parent::walkSelectStatement($AST);
78
79         // Find out the SQL alias of the identifier column of the root entity
80         // It may be possible to make this work with multiple root entities but that
81         // would probably require issuing multiple queries or doing a UNION SELECT
82         // so for now, It's not supported.
83
84         // Get the root entity and alias from the AST fromClause
85         $from = $AST->fromClause->identificationVariableDeclarations;
86         if (count($from) > 1) {
87             throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
88         }
89
90         $rootAlias      = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable;
91         $rootClass      = $this->queryComponents[$rootAlias]['metadata'];
92         $rootIdentifier = $rootClass->identifier;
93
94         // For every identifier, find out the SQL alias by combing through the ResultSetMapping
95         $sqlIdentifier = array();
96         foreach ($rootIdentifier as $property) {
97             if (isset($rootClass->fieldMappings[$property])) {
98                 foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) {
99                     if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) {
100                         $sqlIdentifier[$property] = $alias;
101                     }
102                 }
103             }
104
105             if (isset($rootClass->associationMappings[$property])) {
106                 $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name'];
107
108                 foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) {
109                     if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) {
110                         $sqlIdentifier[$property] = $alias;
111                     }
112                 }
113             }
114         }
115
116         if (count($rootIdentifier) != count($sqlIdentifier)) {
117             throw new \RuntimeException(sprintf(
118                 'Not all identifier properties can be found in the ResultSetMapping: %s',
119                 implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier)))
120             ));
121         }
122
123         // Build the counter query
124         return sprintf('SELECT %s AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table',
125             $this->platform->getCountExpression('*'),
126             implode(', ', $sqlIdentifier),
127             $sql
128         );
129     }
130 }