2012-01-22 16:35:06 +04:00
< ? php
/**
* Doctrine ORM
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE . This license can also be viewed
* at http :// hobodave . com / license . txt
*
* @ category DoctrineExtensions
* @ package DoctrineExtensions\Paginate
* @ author David Abdemoulaie < dave @ hobodave . com >
* @ copyright Copyright ( c ) 2010 David Abdemoulaie ( http :// hobodave . com / )
* @ license http :// hobodave . com / license . txt New BSD License
*/
namespace Doctrine\ORM\Tools\Pagination ;
2012-10-12 15:53:20 +04:00
use Doctrine\DBAL\Types\Type ;
use Doctrine\ORM\Query\TreeWalkerAdapter ;
2014-04-17 23:29:18 +04:00
use Doctrine\ORM\Query\AST\Functions\IdentityFunction ;
2012-10-12 15:53:20 +04:00
use Doctrine\ORM\Query\AST\PathExpression ;
2014-04-17 23:29:18 +04:00
use Doctrine\ORM\Query\AST\SelectExpression ;
use Doctrine\ORM\Query\AST\SelectStatement ;
2012-01-22 16:35:06 +04:00
/**
2012-12-14 17:13:22 +04:00
* Replaces the selectClause of the AST with a SELECT DISTINCT root . id equivalent .
2012-01-22 16:35:06 +04:00
*
* @ category DoctrineExtensions
* @ package DoctrineExtensions\Paginate
* @ author David Abdemoulaie < dave @ hobodave . com >
* @ copyright Copyright ( c ) 2010 David Abdemoulaie ( http :// hobodave . com / )
* @ license http :// hobodave . com / license . txt New BSD License
*/
class LimitSubqueryWalker extends TreeWalkerAdapter
{
/**
2012-12-14 17:13:22 +04:00
* ID type hint .
2012-01-22 16:35:06 +04:00
*/
const IDENTIFIER_TYPE = 'doctrine_paginator.id.type' ;
/**
2012-12-14 17:13:22 +04:00
* Counter for generating unique order column aliases .
*
* @ var int
2012-01-22 16:35:06 +04:00
*/
private $_aliasCounter = 0 ;
/**
* Walks down a SelectStatement AST node , modifying it to retrieve DISTINCT ids
2012-12-14 17:13:22 +04:00
* of the root Entity .
2012-01-22 16:35:06 +04:00
*
* @ param SelectStatement $AST
2012-12-14 17:13:22 +04:00
*
2012-01-22 16:35:06 +04:00
* @ return void
2012-12-14 17:13:22 +04:00
*
* @ throws \RuntimeException
2012-01-22 16:35:06 +04:00
*/
public function walkSelectStatement ( SelectStatement $AST )
{
2014-07-30 17:55:14 +04:00
$queryComponents = $this -> _getQueryComponents ();
// Get the root entity and alias from the AST fromClause
2014-08-28 15:17:25 +04:00
$from = $AST -> fromClause -> identificationVariableDeclarations ;
2014-09-02 10:39:29 +04:00
$fromRoot = reset ( $from );
$rootAlias = $fromRoot -> rangeVariableDeclaration -> aliasIdentificationVariable ;
2014-07-30 17:55:14 +04:00
$rootClass = $queryComponents [ $rootAlias ][ 'metadata' ];
2012-01-22 16:35:06 +04:00
$selectExpressions = array ();
2014-07-30 17:55:14 +04:00
foreach ( $queryComponents as $dqlAlias => $qComp ) {
2012-12-14 17:13:22 +04:00
// Preserve mixed data in query for ordering.
2012-01-22 16:35:06 +04:00
if ( isset ( $qComp [ 'resultVariable' ])) {
$selectExpressions [] = new SelectExpression ( $qComp [ 'resultVariable' ], $dqlAlias );
continue ;
}
}
2014-07-30 17:55:14 +04:00
$identifier = $rootClass -> getSingleIdentifierFieldName ();
2014-08-28 15:17:25 +04:00
2014-07-30 17:55:14 +04:00
if ( isset ( $rootClass -> associationMappings [ $identifier ])) {
2012-05-27 20:33:35 +04:00
throw new \RuntimeException ( " Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator. " );
}
2012-01-22 16:35:06 +04:00
$this -> _getQuery () -> setHint (
self :: IDENTIFIER_TYPE ,
2014-07-30 17:55:14 +04:00
Type :: getType ( $rootClass -> getTypeOfField ( $identifier ))
2012-01-22 16:35:06 +04:00
);
$pathExpression = new PathExpression (
PathExpression :: TYPE_STATE_FIELD | PathExpression :: TYPE_SINGLE_VALUED_ASSOCIATION ,
2014-07-30 17:55:14 +04:00
$rootAlias ,
2012-01-22 16:35:06 +04:00
$identifier
);
$pathExpression -> type = PathExpression :: TYPE_STATE_FIELD ;
array_unshift ( $selectExpressions , new SelectExpression ( $pathExpression , '_dctrn_id' ));
$AST -> selectClause -> selectExpressions = $selectExpressions ;
if ( isset ( $AST -> orderByClause )) {
foreach ( $AST -> orderByClause -> orderByItems as $item ) {
2014-04-17 23:29:18 +04:00
if ( ! $item -> expression instanceof PathExpression ) {
continue ;
2012-01-22 16:35:06 +04:00
}
2014-04-17 23:29:18 +04:00
$AST -> selectClause -> selectExpressions [] = new SelectExpression (
$this -> createSelectExpressionItem ( $item -> expression ),
'_dctrn_ord' . $this -> _aliasCounter ++
);
2012-01-22 16:35:06 +04:00
}
}
$AST -> selectClause -> isDistinct = true ;
}
2014-04-17 23:29:18 +04:00
/**
* Retrieve either an IdentityFunction ( IDENTITY ( u . assoc )) or a state field ( u . name ) .
*
* @ param \Doctrine\ORM\Query\AST\PathExpression $pathExpression
*
* @ return \Doctrine\ORM\Query\AST\Functions\IdentityFunction
*/
private function createSelectExpressionItem ( PathExpression $pathExpression )
{
if ( $pathExpression -> type === PathExpression :: TYPE_SINGLE_VALUED_ASSOCIATION ) {
$identity = new IdentityFunction ( 'identity' );
$identity -> pathExpression = clone $pathExpression ;
return $identity ;
}
return clone $pathExpression ;
}
2012-01-22 16:35:06 +04:00
}