[DDC-1613] Merge KnpLabs/Pagerfanta Pagination into a Doctrine\ORM\Tools\Pagination namespace. Thanks to @hobodave, pablo and the knplabs team for developing and maintaining this code.
This commit is contained in:
parent
358bb2ec33
commit
775071e1ff
80
lib/Doctrine/ORM/Tools/Pagination/CountWalker.php
Normal file
80
lib/Doctrine/ORM/Tools/Pagination/CountWalker.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?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\TreeWalkerAdapter,
|
||||
Doctrine\ORM\Query\AST\SelectStatement,
|
||||
Doctrine\ORM\Query\AST\SelectExpression,
|
||||
Doctrine\ORM\Query\AST\PathExpression,
|
||||
Doctrine\ORM\Query\AST\AggregateExpression;
|
||||
|
||||
/**
|
||||
* Replaces the selectClause of the AST with a COUNT statement
|
||||
*
|
||||
* @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 CountWalker extends TreeWalkerAdapter
|
||||
{
|
||||
/**
|
||||
* Distinct mode hint name
|
||||
*/
|
||||
const HINT_DISTINCT = 'doctrine_paginator.distinct';
|
||||
|
||||
/**
|
||||
* Walks down a SelectStatement AST node, modifying it to retrieve a COUNT
|
||||
*
|
||||
* @param SelectStatement $AST
|
||||
* @return void
|
||||
*/
|
||||
public function walkSelectStatement(SelectStatement $AST)
|
||||
{
|
||||
$rootComponents = array();
|
||||
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
|
||||
$isParent = array_key_exists('parent', $qComp)
|
||||
&& $qComp['parent'] === null
|
||||
&& $qComp['nestingLevel'] == 0
|
||||
;
|
||||
if ($isParent) {
|
||||
$rootComponents[] = array($dqlAlias => $qComp);
|
||||
}
|
||||
}
|
||||
if (count($rootComponents) > 1) {
|
||||
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
|
||||
}
|
||||
$root = reset($rootComponents);
|
||||
$parentName = key($root);
|
||||
$parent = current($root);
|
||||
|
||||
$pathExpression = new PathExpression(
|
||||
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
|
||||
$parent['metadata']->getSingleIdentifierFieldName()
|
||||
);
|
||||
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
|
||||
|
||||
$distinct = $this->_getQuery()->getHint(self::HINT_DISTINCT);
|
||||
$AST->selectClause->selectExpressions = array(
|
||||
new SelectExpression(
|
||||
new AggregateExpression('count', $pathExpression, $distinct), null
|
||||
)
|
||||
);
|
||||
|
||||
// ORDER BY is not needed, only increases query execution through unnecessary sorting.
|
||||
$AST->orderByClause = null;
|
||||
}
|
||||
}
|
||||
|
115
lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php
Normal file
115
lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php
Normal file
@ -0,0 +1,115 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\DBAL\Types\Type,
|
||||
Doctrine\ORM\Query\TreeWalkerAdapter,
|
||||
Doctrine\ORM\Query\AST\SelectStatement,
|
||||
Doctrine\ORM\Query\AST\SelectExpression,
|
||||
Doctrine\ORM\Query\AST\PathExpression,
|
||||
Doctrine\ORM\Query\AST\AggregateExpression;
|
||||
|
||||
/**
|
||||
* Replaces the selectClause of the AST with a SELECT DISTINCT root.id equivalent
|
||||
*
|
||||
* @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
|
||||
{
|
||||
/**
|
||||
* ID type hint
|
||||
*/
|
||||
const IDENTIFIER_TYPE = 'doctrine_paginator.id.type';
|
||||
|
||||
/**
|
||||
* @var int Counter for generating unique order column aliases
|
||||
*/
|
||||
private $_aliasCounter = 0;
|
||||
|
||||
/**
|
||||
* Walks down a SelectStatement AST node, modifying it to retrieve DISTINCT ids
|
||||
* of the root Entity
|
||||
*
|
||||
* @param SelectStatement $AST
|
||||
* @return void
|
||||
*/
|
||||
public function walkSelectStatement(SelectStatement $AST)
|
||||
{
|
||||
$parent = null;
|
||||
$parentName = null;
|
||||
$selectExpressions = array();
|
||||
|
||||
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
|
||||
// preserve mixed data in query for ordering
|
||||
if (isset($qComp['resultVariable'])) {
|
||||
$selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
|
||||
$parent = $qComp;
|
||||
$parentName = $dqlAlias;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$identifier = $parent['metadata']->getSingleIdentifierFieldName();
|
||||
$this->_getQuery()->setHint(
|
||||
self::IDENTIFIER_TYPE,
|
||||
Type::getType($parent['metadata']->getTypeOfField($identifier))
|
||||
);
|
||||
|
||||
$pathExpression = new PathExpression(
|
||||
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
|
||||
$parentName,
|
||||
$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) {
|
||||
if ($item->expression instanceof PathExpression) {
|
||||
$pathExpression = new PathExpression(
|
||||
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
|
||||
$item->expression->identificationVariable,
|
||||
$item->expression->field
|
||||
);
|
||||
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
|
||||
$AST->selectClause->selectExpressions[] = new SelectExpression(
|
||||
$pathExpression,
|
||||
'_dctrn_ord' . $this->_aliasCounter++
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$AST->selectClause->isDistinct = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
180
lib/Doctrine/ORM/Tools/Pagination/Paginator.php
Normal file
180
lib/Doctrine/ORM/Tools/Pagination/Paginator.php
Normal file
@ -0,0 +1,180 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Tools\Pagination;
|
||||
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\NoResultException;
|
||||
use Doctrine\ORM\Tools\Pagination\WhereInWalker;
|
||||
use Doctrine\ORM\Tools\Pagination\CountWalker;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use ArrayIterator;
|
||||
|
||||
/**
|
||||
* Paginator
|
||||
*
|
||||
* The paginator can handle various complex scenarios with DQL.
|
||||
*
|
||||
* @author Pablo Díez <pablodip@gmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @license New BSD
|
||||
*/
|
||||
class Paginator implements \Countable, \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var Query
|
||||
*/
|
||||
private $query;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $fetchJoinCollection;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $count;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Query|QueryBuilder $query A Doctrine ORM query or query builder.
|
||||
* @param Boolean $fetchJoinCollection Whether the query joins a collection (true by default).
|
||||
*/
|
||||
public function __construct($query, $fetchJoinCollection = true)
|
||||
{
|
||||
if ($query instanceof QueryBuilder) {
|
||||
$query = $query->getQuery();
|
||||
}
|
||||
|
||||
$this->query = $query;
|
||||
$this->fetchJoinCollection = (Boolean) $fetchJoinCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the query
|
||||
*
|
||||
* @return Query
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the query joins a collection.
|
||||
*
|
||||
* @return Boolean Whether the query joins a collection.
|
||||
*/
|
||||
public function getFetchJoinCollection()
|
||||
{
|
||||
return $this->fetchJoinCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function count()
|
||||
{
|
||||
if ($this->count === null) {
|
||||
/* @var $countQuery Query */
|
||||
$countQuery = $this->cloneQuery($this->query);
|
||||
|
||||
if ( ! $countQuery->getHint(CountWalker::HINT_DISTINCT)) {
|
||||
$countQuery->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
}
|
||||
|
||||
$countQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
|
||||
$countQuery->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
try {
|
||||
$data = $countQuery->getScalarResult();
|
||||
$data = array_map('current', $data);
|
||||
$this->count = array_sum($data);
|
||||
} catch(NoResultException $e) {
|
||||
$this->count = 0;
|
||||
}
|
||||
}
|
||||
return $this->count;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
$offset = $this->query->getFirstResult();
|
||||
$length = $this->query->getMaxResults();
|
||||
|
||||
if ($this->fetchJoinCollection) {
|
||||
$subQuery = $this->cloneQuery($this->query);
|
||||
$subQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'))
|
||||
->setFirstResult($offset)
|
||||
->setMaxResults($length);
|
||||
|
||||
$ids = array_map('current', $subQuery->getScalarResult());
|
||||
|
||||
$whereInQuery = $this->cloneQuery($this->query);
|
||||
// don't do this for an empty id array
|
||||
if (count($ids) > 0) {
|
||||
$namespace = WhereInWalker::PAGINATOR_ID_ALIAS;
|
||||
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, count($ids));
|
||||
$whereInQuery->setFirstResult(null)->setMaxResults(null);
|
||||
foreach ($ids as $i => $id) {
|
||||
$i++;
|
||||
$whereInQuery->setParameter("{$namespace}_{$i}", $id);
|
||||
}
|
||||
}
|
||||
|
||||
$result = $whereInQuery->getResult($this->query->getHydrationMode());
|
||||
} else {
|
||||
$result = $this->cloneQuery($this->query)
|
||||
->setMaxResults($length)
|
||||
->setFirstResult($offset)
|
||||
->getResult($this->query->getHydrationMode())
|
||||
;
|
||||
}
|
||||
return new \ArrayIterator($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones a query.
|
||||
*
|
||||
* @param Query $query The query.
|
||||
*
|
||||
* @return Query The cloned query.
|
||||
*/
|
||||
private function cloneQuery(Query $query)
|
||||
{
|
||||
/* @var $cloneQuery Query */
|
||||
$cloneQuery = clone $query;
|
||||
$cloneQuery->setParameters($query->getParameters());
|
||||
foreach ($query->getHints() as $name => $value) {
|
||||
$cloneQuery->setHint($name, $value);
|
||||
}
|
||||
|
||||
return $cloneQuery;
|
||||
}
|
||||
}
|
||||
|
142
lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php
Normal file
142
lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php
Normal file
@ -0,0 +1,142 @@
|
||||
<?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;
|
||||
|
||||
use Doctrine\ORM\Query\AST\ArithmeticExpression,
|
||||
Doctrine\ORM\Query\AST\SimpleArithmeticExpression,
|
||||
Doctrine\ORM\Query\TreeWalkerAdapter,
|
||||
Doctrine\ORM\Query\AST\SelectStatement,
|
||||
Doctrine\ORM\Query\AST\PathExpression,
|
||||
Doctrine\ORM\Query\AST\InExpression,
|
||||
Doctrine\ORM\Query\AST\NullComparisonExpression,
|
||||
Doctrine\ORM\Query\AST\InputParameter,
|
||||
Doctrine\ORM\Query\AST\ConditionalPrimary,
|
||||
Doctrine\ORM\Query\AST\ConditionalTerm,
|
||||
Doctrine\ORM\Query\AST\ConditionalExpression,
|
||||
Doctrine\ORM\Query\AST\ConditionalFactor,
|
||||
Doctrine\ORM\Query\AST\WhereClause;
|
||||
|
||||
/**
|
||||
* Replaces the whereClause of the AST with a WHERE id IN (:foo_1, :foo_2) equivalent
|
||||
*
|
||||
* @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 WhereInWalker extends TreeWalkerAdapter
|
||||
{
|
||||
/**
|
||||
* ID Count hint name
|
||||
*/
|
||||
const HINT_PAGINATOR_ID_COUNT = 'doctrine.id.count';
|
||||
|
||||
/**
|
||||
* Primary key alias for query
|
||||
*/
|
||||
const PAGINATOR_ID_ALIAS = 'dpid';
|
||||
|
||||
/**
|
||||
* Replaces the whereClause in the AST
|
||||
*
|
||||
* Generates a clause equivalent to WHERE IN (:dpid_1, :dpid_2, ...)
|
||||
*
|
||||
* The parameter namespace (dpid) is defined by
|
||||
* the PAGINATOR_ID_ALIAS
|
||||
*
|
||||
* The total number of parameters is retrieved from
|
||||
* the HINT_PAGINATOR_ID_COUNT query hint
|
||||
*
|
||||
* @param SelectStatement $AST
|
||||
* @return void
|
||||
*/
|
||||
public function walkSelectStatement(SelectStatement $AST)
|
||||
{
|
||||
$rootComponents = array();
|
||||
foreach ($this->_getQueryComponents() AS $dqlAlias => $qComp) {
|
||||
$isParent = array_key_exists('parent', $qComp)
|
||||
&& $qComp['parent'] === null
|
||||
&& $qComp['nestingLevel'] == 0
|
||||
;
|
||||
if ($isParent) {
|
||||
$rootComponents[] = array($dqlAlias => $qComp);
|
||||
}
|
||||
}
|
||||
if (count($rootComponents) > 1) {
|
||||
throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
|
||||
}
|
||||
$root = reset($rootComponents);
|
||||
$parentName = key($root);
|
||||
$parent = current($root);
|
||||
|
||||
$pathExpression = new PathExpression(
|
||||
PathExpression::TYPE_STATE_FIELD, $parentName, $parent['metadata']->getSingleIdentifierFieldName()
|
||||
);
|
||||
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
|
||||
|
||||
$count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT);
|
||||
|
||||
if ($count > 0) {
|
||||
$arithmeticExpression = new ArithmeticExpression();
|
||||
$arithmeticExpression->simpleArithmeticExpression = new SimpleArithmeticExpression(
|
||||
array($pathExpression)
|
||||
);
|
||||
$expression = new InExpression($arithmeticExpression);
|
||||
$ns = self::PAGINATOR_ID_ALIAS;
|
||||
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
$expression->literals[] = new InputParameter(":{$ns}_$i");
|
||||
}
|
||||
} else {
|
||||
$expression = new NullComparisonExpression($pathExpression);
|
||||
$expression->not = false;
|
||||
}
|
||||
|
||||
$conditionalPrimary = new ConditionalPrimary;
|
||||
$conditionalPrimary->simpleConditionalExpression = $expression;
|
||||
if ($AST->whereClause) {
|
||||
if ($AST->whereClause->conditionalExpression instanceof ConditionalTerm) {
|
||||
$AST->whereClause->conditionalExpression->conditionalFactors[] = $conditionalPrimary;
|
||||
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalPrimary) {
|
||||
$AST->whereClause->conditionalExpression = new ConditionalExpression(array(
|
||||
new ConditionalTerm(array(
|
||||
$AST->whereClause->conditionalExpression,
|
||||
$conditionalPrimary
|
||||
))
|
||||
));
|
||||
} elseif ($AST->whereClause->conditionalExpression instanceof ConditionalExpression) {
|
||||
$tmpPrimary = new ConditionalPrimary;
|
||||
$tmpPrimary->conditionalExpression = $AST->whereClause->conditionalExpression;
|
||||
$AST->whereClause->conditionalExpression = new ConditionalTerm(array(
|
||||
$tmpPrimary,
|
||||
$conditionalPrimary
|
||||
));
|
||||
}
|
||||
} else {
|
||||
$AST->whereClause = new WhereClause(
|
||||
new ConditionalExpression(array(
|
||||
new ConditionalTerm(array(
|
||||
$conditionalPrimary
|
||||
))
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
105
tests/Doctrine/Tests/ORM/Functional/PaginationTest.php
Normal file
105
tests/Doctrine/Tests/ORM/Functional/PaginationTest.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional;
|
||||
|
||||
use Doctrine\ORM\Tools\SchemaTool;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\Tests\Models\CMS\CmsUser;
|
||||
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
|
||||
use Doctrine\Tests\Models\CMS\CmsAddress;
|
||||
use Doctrine\Tests\Models\CMS\CmsGroup;
|
||||
use Doctrine\Tests\Models\CMS\CmsArticle;
|
||||
use Doctrine\Tests\Models\CMS\CmsComment;
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
|
||||
/**
|
||||
* @group DDC-1613
|
||||
*/
|
||||
class PaginationTest extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
protected function setUp()
|
||||
{
|
||||
$this->useModelSet('cms');
|
||||
parent::setUp();
|
||||
$this->populate();
|
||||
}
|
||||
|
||||
public function testCountSimpleWithoutJoin()
|
||||
{
|
||||
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$query = $this->_em->createQuery($dql);
|
||||
|
||||
$paginator = new Paginator($query);
|
||||
$this->assertEquals(3, count($paginator));
|
||||
}
|
||||
|
||||
public function testCountWithFetchJoin()
|
||||
{
|
||||
$dql = "SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g";
|
||||
$query = $this->_em->createQuery($dql);
|
||||
|
||||
$paginator = new Paginator($query);
|
||||
$this->assertEquals(3, count($paginator));
|
||||
}
|
||||
|
||||
public function testIterateSimpleWithoutJoinFetchJoinHandlingOff()
|
||||
{
|
||||
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$query = $this->_em->createQuery($dql);
|
||||
|
||||
$paginator = new Paginator($query, false);
|
||||
|
||||
$data = array();
|
||||
foreach ($paginator as $user) {
|
||||
$data[] = $user;
|
||||
}
|
||||
$this->assertEquals(3, count($data));
|
||||
}
|
||||
|
||||
public function testIterateSimpleWithoutJoinFetchJoinHandlingOn()
|
||||
{
|
||||
$dql = "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u";
|
||||
$query = $this->_em->createQuery($dql);
|
||||
|
||||
$paginator = new Paginator($query, true);
|
||||
|
||||
$data = array();
|
||||
foreach ($paginator as $user) {
|
||||
$data[] = $user;
|
||||
}
|
||||
$this->assertEquals(3, count($data));
|
||||
}
|
||||
|
||||
public function testIterateWithFetchJoin()
|
||||
{
|
||||
$dql = "SELECT u,g FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g";
|
||||
$query = $this->_em->createQuery($dql);
|
||||
|
||||
$paginator = new Paginator($query, true);
|
||||
|
||||
$data = array();
|
||||
foreach ($paginator as $user) {
|
||||
$data[] = $user;
|
||||
}
|
||||
$this->assertEquals(3, count($data));
|
||||
}
|
||||
|
||||
public function populate()
|
||||
{
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$user = new CmsUser();
|
||||
$user->name = "Name$i";
|
||||
$user->username = "username$i";
|
||||
$user->status = "active";
|
||||
$this->_em->persist($user);
|
||||
|
||||
for ($j = 0; $j < 3; $j++) {;
|
||||
$group = new CmsGroup();
|
||||
$group->name = "group$j";
|
||||
$user->addGroup($group);
|
||||
$this->_em->persist($group);
|
||||
}
|
||||
}
|
||||
$this->_em->flush();
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Tools\Pagination;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Tools\Pagination\CountWalker;
|
||||
|
||||
/**
|
||||
* @group DDC-1613
|
||||
*/
|
||||
class CountWalkerTest extends PaginationTestCase
|
||||
{
|
||||
public function testCountQuery()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a');
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
|
||||
$query->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
$query->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT count(DISTINCT b0_.id) AS sclr0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id", $query->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQuery_MixedResultsWithName()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a');
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
|
||||
$query->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
$query->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT count(DISTINCT a0_.id) AS sclr0 FROM Author a0_", $query->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQuery_KeepsGroupBy()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT b FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost b GROUP BY b.id');
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
|
||||
$query->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
$query->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT count(DISTINCT b0_.id) AS sclr0 FROM BlogPost b0_ GROUP BY b0_.id", $query->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQuery_RemovesOrderBy()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a ORDER BY a.name');
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
|
||||
$query->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
$query->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT count(DISTINCT b0_.id) AS sclr0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id", $query->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQuery_RemovesLimits()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN p.category c JOIN p.author a');
|
||||
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
|
||||
$query->setHint(CountWalker::HINT_DISTINCT, true);
|
||||
$query->setFirstResult(null)->setMaxResults(null);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT count(DISTINCT b0_.id) AS sclr0 FROM BlogPost b0_ INNER JOIN Category c1_ ON b0_.category_id = c1_.id INNER JOIN Author a2_ ON b0_.author_id = a2_.id", $query->getSql()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Tools\Pagination;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
|
||||
/**
|
||||
* @group DDC-1613
|
||||
*/
|
||||
class LimitSubqueryWalkerTest extends PaginationTestCase
|
||||
{
|
||||
public function testLimitSubquery()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT p, c, a FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN p.category c JOIN p.author a');
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'));
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT DISTINCT m0_.id AS id0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON m0_.category_id = c1_.id INNER JOIN Author a2_ ON m0_.author_id = a2_.id", $limitQuery->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQuery_MixedResultsWithName()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a');
|
||||
$limitQuery = clone $query;
|
||||
$limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'));
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT DISTINCT a0_.id AS id0, sum(a0_.name) AS sclr1 FROM Author a0_", $limitQuery->getSql()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
142
tests/Doctrine/Tests/ORM/Tools/Pagination/PaginationTestCase.php
Normal file
142
tests/Doctrine/Tests/ORM/Tools/Pagination/PaginationTestCase.php
Normal file
@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Tools\Pagination;
|
||||
|
||||
use Doctrine\Tests\OrmTestCase;
|
||||
|
||||
abstract class PaginationTestCase extends OrmTestCase
|
||||
{
|
||||
public $entityManager;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
$this->entityManager = $this->_getTestEntityManager();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class MyBlogPost
|
||||
{
|
||||
|
||||
/** @Id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Author")
|
||||
*/
|
||||
public $author;
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Category")
|
||||
*/
|
||||
public $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class MyAuthor
|
||||
{
|
||||
|
||||
/** @Id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class MyCategory
|
||||
{
|
||||
|
||||
/** @id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class BlogPost
|
||||
{
|
||||
|
||||
/** @Id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Author")
|
||||
*/
|
||||
public $author;
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Category")
|
||||
*/
|
||||
public $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Author
|
||||
{
|
||||
|
||||
/** @Id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
/** @Column(type="string") */
|
||||
public $name;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Person
|
||||
{
|
||||
|
||||
/** @Id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
/** @Column(type="string") */
|
||||
public $name;
|
||||
/** @Column(type="string") */
|
||||
public $biography;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Category
|
||||
{
|
||||
|
||||
/** @id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** @Entity @Table(name="groups") */
|
||||
class Group
|
||||
{
|
||||
|
||||
/** @Id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
/** @ManyToMany(targetEntity="User", mappedBy="groups") */
|
||||
public $users;
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
|
||||
/** @Id @column(type="integer") @generatedValue */
|
||||
public $id;
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group", inversedBy="users")
|
||||
* @JoinTable(
|
||||
* name="user_group",
|
||||
* joinColumns = {@JoinColumn(name="user_id", referencedColumnName="id")},
|
||||
* inverseJoinColumns = {@JoinColumn(name="group_id", referencedColumnName="id")}
|
||||
* )
|
||||
*/
|
||||
public $groups;
|
||||
}
|
111
tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php
Normal file
111
tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php
Normal file
@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Tools\Pagination;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Tools\Pagination\WhereInWalker;
|
||||
|
||||
/**
|
||||
* @group DDC-1613
|
||||
*/
|
||||
class WhereInWalkerTest extends PaginationTestCase
|
||||
{
|
||||
public function testWhereInQuery_NoWhere()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g'
|
||||
);
|
||||
$whereInQuery = clone $query;
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT u0_.id AS id0, g1_.id AS id1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE u0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $whereInQuery->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testCountQuery_MixedResultsWithName()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT a, sum(a.name) as foo FROM Doctrine\Tests\ORM\Tools\Pagination\Author a'
|
||||
);
|
||||
$whereInQuery = clone $query;
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT a0_.id AS id0, a0_.name AS name1, sum(a0_.name) AS sclr2 FROM Author a0_ WHERE a0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $whereInQuery->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhereInQuery_SingleWhere()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g WHERE 1 = 1'
|
||||
);
|
||||
$whereInQuery = clone $query;
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT u0_.id AS id0, g1_.id AS id1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE 1 = 1 AND u0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $whereInQuery->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhereInQuery_MultipleWhereWithAnd()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g WHERE 1 = 1 AND 2 = 2'
|
||||
);
|
||||
$whereInQuery = clone $query;
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT u0_.id AS id0, g1_.id AS id1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE 1 = 1 AND 2 = 2 AND u0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $whereInQuery->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhereInQuery_MultipleWhereWithOr()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g WHERE 1 = 1 OR 2 = 2'
|
||||
);
|
||||
$whereInQuery = clone $query;
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT u0_.id AS id0, g1_.id AS id1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (1 = 1 OR 2 = 2) AND u0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $whereInQuery->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhereInQuery_MultipleWhereWithMixed_1()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g WHERE (1 = 1 OR 2 = 2) AND 3 = 3'
|
||||
);
|
||||
$whereInQuery = clone $query;
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT u0_.id AS id0, g1_.id AS id1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (1 = 1 OR 2 = 2) AND 3 = 3 AND u0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $whereInQuery->getSql()
|
||||
);
|
||||
}
|
||||
|
||||
public function testWhereInQuery_MultipleWhereWithMixed_2()
|
||||
{
|
||||
$query = $this->entityManager->createQuery(
|
||||
'SELECT u, g FROM Doctrine\Tests\ORM\Tools\Pagination\User u JOIN u.groups g WHERE 1 = 1 AND 2 = 2 OR 3 = 3'
|
||||
);
|
||||
$whereInQuery = clone $query;
|
||||
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
|
||||
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);
|
||||
|
||||
$this->assertEquals(
|
||||
"SELECT u0_.id AS id0, g1_.id AS id1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (1 = 1 AND 2 = 2 OR 3 = 3) AND u0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $whereInQuery->getSql()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user