Rajout de doctrine/orm
[zf2.biz/application_blanche.git] / vendor / doctrine / orm / lib / Doctrine / ORM / Tools / Pagination / CountOutputWalker.php
diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/CountOutputWalker.php
new file mode 100644 (file)
index 0000000..098934e
--- /dev/null
@@ -0,0 +1,130 @@
+<?php
+/**
+ * Doctrine ORM
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.txt.
+ * If you did not receive a copy of the license and are unable to
+ * obtain it through the world-wide-web, please send an email
+ * to kontakt@beberlei.de so I can send you a copy immediately.
+ */
+
+namespace Doctrine\ORM\Tools\Pagination;
+
+use Doctrine\ORM\Query\SqlWalker,
+    Doctrine\ORM\Query\AST\SelectStatement;
+
+/**
+ * Wrap the query in order to accurately count the root objects
+ *
+ * Given a DQL like `SELECT u FROM User u` it will generate an SQL query like:
+ * SELECT COUNT(*) (SELECT DISTINCT <id> FROM (<original SQL>))
+ *
+ * Works with composite keys but cannot deal with queries that have multiple
+ * root entities (e.g. `SELECT f, b from Foo, Bar`)
+ *
+ * @author Sander Marechal <s.marechal@jejik.com>
+ */
+class CountOutputWalker extends SqlWalker
+{
+    /**
+     * @var Doctrine\DBAL\Platforms\AbstractPlatform
+     */
+    private $platform;
+
+    /**
+     * @var Doctrine\ORM\Query\ResultSetMapping
+     */
+    private $rsm;
+
+    /**
+     * @var array
+     */
+    private $queryComponents;
+
+    /**
+     * Constructor. Stores various parameters that are otherwise unavailable
+     * because Doctrine\ORM\Query\SqlWalker keeps everything private without
+     * accessors.
+     *
+     * @param Doctrine\ORM\Query $query
+     * @param Doctrine\ORM\Query\ParserResult $parserResult
+     * @param array $queryComponents
+     */
+    public function __construct($query, $parserResult, array $queryComponents)
+    {
+        $this->platform = $query->getEntityManager()->getConnection()->getDatabasePlatform();
+        $this->rsm = $parserResult->getResultSetMapping();
+        $this->queryComponents = $queryComponents;
+
+        parent::__construct($query, $parserResult, $queryComponents);
+    }
+
+    /**
+     * Walks down a SelectStatement AST node, wrapping it in a COUNT (SELECT DISTINCT)
+     *
+     * Note that the ORDER BY clause is not removed. Many SQL implementations (e.g. MySQL)
+     * are able to cache subqueries. By keeping the ORDER BY clause intact, the limitSubQuery
+     * that will most likely be executed next can be read from the native SQL cache.
+     *
+     * @param SelectStatement $AST
+     * @return string
+     */
+    public function walkSelectStatement(SelectStatement $AST)
+    {
+        $sql = parent::walkSelectStatement($AST);
+
+        // Find out the SQL alias of the identifier column of the root entity
+        // It may be possible to make this work with multiple root entities but that
+        // would probably require issuing multiple queries or doing a UNION SELECT
+        // so for now, It's not supported.
+
+        // 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");
+        }
+
+        $rootAlias      = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable;
+        $rootClass      = $this->queryComponents[$rootAlias]['metadata'];
+        $rootIdentifier = $rootClass->identifier;
+
+        // For every identifier, find out the SQL alias by combing through the ResultSetMapping
+        $sqlIdentifier = array();
+        foreach ($rootIdentifier as $property) {
+            if (isset($rootClass->fieldMappings[$property])) {
+                foreach (array_keys($this->rsm->fieldMappings, $property) as $alias) {
+                    if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) {
+                        $sqlIdentifier[$property] = $alias;
+                    }
+                }
+            }
+
+            if (isset($rootClass->associationMappings[$property])) {
+                $joinColumn = $rootClass->associationMappings[$property]['joinColumns'][0]['name'];
+
+                foreach (array_keys($this->rsm->metaMappings, $joinColumn) as $alias) {
+                    if ($this->rsm->columnOwnerMap[$alias] == $rootAlias) {
+                        $sqlIdentifier[$property] = $alias;
+                    }
+                }
+            }
+        }
+
+        if (count($rootIdentifier) != count($sqlIdentifier)) {
+            throw new \RuntimeException(sprintf(
+                'Not all identifier properties can be found in the ResultSetMapping: %s',
+                implode(', ', array_diff($rootIdentifier, array_keys($sqlIdentifier)))
+            ));
+        }
+
+        // Build the counter query
+        return sprintf('SELECT %s AS dctrn_count FROM (SELECT DISTINCT %s FROM (%s) dctrn_result) dctrn_table',
+            $this->platform->getCountExpression('*'),
+            implode(', ', $sqlIdentifier),
+            $sql
+        );
+    }
+}