X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=vendor%2Fdoctrine%2Form%2Flib%2FDoctrine%2FORM%2FTools%2FPagination%2FLimitSubqueryOutputWalker.php;fp=vendor%2Fdoctrine%2Form%2Flib%2FDoctrine%2FORM%2FTools%2FPagination%2FLimitSubqueryOutputWalker.php;h=2c79a5accfb7f920802e16058cbf99d375bfcea0;hb=8b04b2d11798dee4f3e1358e4f43e97a6df851f6;hp=0000000000000000000000000000000000000000;hpb=73568cf05a785a45f94ca3f2351d9e07bf917958;p=zf2.biz%2Fapplication_blanche.git diff --git a/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php new file mode 100644 index 0000000..2c79a5a --- /dev/null +++ b/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryOutputWalker.php @@ -0,0 +1,153 @@ + FROM () LIMIT x OFFSET y + * + * 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 + */ +class LimitSubqueryOutputWalker extends SqlWalker +{ + /** + * @var Doctrine\DBAL\Platforms\AbstractPlatform + */ + private $platform; + + /** + * @var Doctrine\ORM\Query\ResultSetMapping + */ + private $rsm; + + /** + * @var array + */ + private $queryComponents; + + /** + * @var int + */ + private $firstResult; + + /** + * @var int + */ + private $maxResults; + + /** + * 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; + + // Reset limit and offset + $this->firstResult = $query->getFirstResult(); + $this->maxResults = $query->getMaxResults(); + $query->setFirstResult(null)->setMaxResults(null); + + parent::__construct($query, $parserResult, $queryComponents); + } + + /** + * Walks down a SelectStatement AST node, wrapping it in a SELECT DISTINCT + * + * @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 + $sql = sprintf('SELECT DISTINCT %s FROM (%s) dctrn_result', + implode(', ', $sqlIdentifier), $sql); + + // Apply the limit and offset + $sql = $this->platform->modifyLimitQuery( + $sql, $this->maxResults, $this->firstResult + ); + + // Add the columns to the ResultSetMapping. It's not really nice but + // it works. Preferably I'd clear the RSM or simply create a new one + // but that is not possible from inside the output walker, so we dirty + // up the one we have. + foreach ($sqlIdentifier as $property => $alias) { + $this->rsm->addScalarResult($alias, $property); + } + + return $sql; + } +}