Skip to content

Commit 756c445

Browse files
committed
Merge pull request #115 from KnpLabs/refactor
Use doctrine Paginator if available
2 parents 3f2fe97 + 861c2a0 commit 756c445

File tree

24 files changed

+301
-174
lines changed

24 files changed

+301
-174
lines changed

.gitignore

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
tests/phpunit.xml
2-
tests/temp/*.php
3-
vendor/*
1+
/tests/phpunit.xml
2+
/tests/temp/*.php
3+
/vendor
44
tags
5+
/composer.lock

.travis.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ php:
77
- 5.5
88
- 5.6
99
- hhvm
10-
10+
1111
matrix:
1212
allow_failures:
1313
- php: hhvm
14-
14+
1515
before_script:
16-
- php bin/vendors.php
16+
- echo 'extension=mongo.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
17+
- composer install --prefer-dist
1718

1819
script: phpunit -c tests

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ PHPUnit 3.5 or newer is required.
1111
To setup and run tests follow these steps:
1212

1313
- go to the root directory of components
14-
- run: **php bin/vendors.php**
15-
- run: **phpunit -c tests**
14+
- run:
15+
16+
composer install
17+
vendor/bin/phpunit -c tests
1618

bin/vendors.php

-50
This file was deleted.

composer.json

+12-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
"require": {
2121
"php": ">=5.3.2"
2222
},
23+
"require-dev": {
24+
"symfony/event-dispatcher": "~2.5",
25+
"doctrine/orm": "~2.4",
26+
"doctrine/mongodb-odm": "~1.0@beta",
27+
"phpunit/phpunit": "~4.2"
28+
},
2329

2430
"suggest": {
2531
"doctrine/orm" : "to allow usage pagination with Doctrine ORM",
@@ -38,7 +44,12 @@
3844

3945
"autoload": {
4046
"psr-0": {
41-
"Knp\\Component": "src/"
47+
"Knp\\Component": "src"
48+
}
49+
},
50+
"autoload-dev": {
51+
"psr-0": {
52+
"Test": "tests"
4253
}
4354
}
4455
}

doc/pager/config.md

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## Configuration
2+
3+
4+
Some subscribers will take into account some options.
5+
These options can be passed as the 4th argument of `Paginator::paginate()`.
6+
7+
For example, Doctrine ORM subscriber will generate different sql queries if the `distinct` options changes.
8+
9+
10+
The list of existing options are:
11+
12+
| name | type | default value | subscribers |
13+
| -------------------------- | ------ | ------------- | ----------------------------------------------- |
14+
| wrap-queries | bool | false | orm QuerySubscriber, orm QueryBuilderSubscriber |
15+
| distinct | bool | true | QuerySubscriber, QueryBuilderSubscriber |
16+
| pageParameterName | string | page | SortableSubscriber |
17+
| defaultSortFieldName | string | | SortableSubscriber |
18+
| defaultSortDirection | string | asc | SortableSubscriber |
19+
| sortFieldWhitelist | array | [] | SortableSubscriber |
20+
| sortFieldParameterName | string | sort | SortableSubscriber |
21+
| sortDirectionParameterName | string | sort | SortableSubscriber |
22+
| filterFieldParameterName | string | filterParam | FiltrationSubscriber |
23+
| filterValueParameterName | string | filterValue | FiltrationSubscriber |
24+
25+
26+
## Noticeable behaviors of some options
27+
28+
### `distinct`
29+
30+
If set to true, will add a distinct sql keyword on orm queries to ensuire unicity of counted results
31+
32+
33+
### `wrap-queries`
34+
35+
If set to true, will take advantage of doctrine 2.3 output walkers by using subqueries to handle composite keys and HAVING queries.
36+
This can considerably impact performances depending on the query and the platform, you will have to consider it at some point.
37+

doc/pager/intro.md

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Why reinvent the wheel? Can someone me tell what the definition of **wheel** is
2929
- Separation of conserns, paginator is responsible for generating the pagination view only,
3030
pagination view - for representation purposes.
3131
- Does not require initializing specific adapters
32+
- [configurable](config.md)
3233

3334
## Usage examples:
3435

doc/pager/usage.md

+1-44
Original file line numberDiff line numberDiff line change
@@ -4,50 +4,7 @@ This tutorial will cover installation and usage examples.
44

55
## Installation
66

7-
Initially, all what is needed is:
8-
9-
- >= php5.3.2
10-
- Symfony **EventDispatcher** component and if you do not have autoloader, I recommend
11-
**ClassLoader** from the same symfony components
12-
- this repository
13-
14-
Now somewhere in you third party vendor directory download mentioned dependencies:
15-
**Note:** if you are in your project root and you have [git](http://help.github.com/set-up-git-redirect)
16-
installed, **vendor** directory is the location where these components will be installed.
17-
18-
- run **git clone git://github.com/knplabs/knp-components.git vendor/knp-components**
19-
- run **git clone git://github.com/symfony/EventDispatcher.git vendor/Symfony/Component/EventDispatcher**
20-
- run **git clone git://github.com/symfony/ClassLoader.git vendor/Symfony/Component/ClassLoader**
21-
22-
To initially autoload these components, you will need to include
23-
**vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php**
24-
25-
``` php
26-
<?php
27-
// file: autoloader.php
28-
// taking into account that this autoloader is in the same directory as vendor folder
29-
require_once __DIR__.'/vendor/Symfony/Component/ClassLoader/UniversalClassLoader.php';
30-
31-
$loader = new Symfony\Component\ClassLoader\UniversalClassLoader;
32-
$loader->registerNamespaces(array(
33-
'Symfony\\Component' => __DIR__.'/vendor',
34-
'Knp\\Component' => __DIR__.'/vendor/knp-components/src'
35-
));
36-
$loader->register();
37-
```
38-
39-
Next, usually **index.php** file is the starting point. Lets create it in the same
40-
project root directory as **autoloader.php**
41-
42-
``` php
43-
<?php
44-
// file: index.php
45-
include 'autoloader.php';
46-
47-
// usage examples will continue here
48-
```
49-
50-
In general now you can start using the paginator..
7+
composer require "knplabs/knp-components:~1.2"
518

529
## Basic usage
5310

src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ORM/Query/CountWalker.php

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* @author David Abdemoulaie <[email protected]>
2828
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
2929
* @license http://hobodave.com/license.txt New BSD License
30+
* @deprecated
3031
*/
3132
class CountWalker extends TreeWalkerAdapter
3233
{

src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ORM/Query/LimitSubqueryWalker.php

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* @author David Abdemoulaie <[email protected]>
1515
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
1616
* @license http://hobodave.com/license.txt New BSD License
17+
* @deprecated
1718
*/
1819

1920
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\Query;

src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ORM/Query/WhereInWalker.php

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
* @author David Abdemoulaie <[email protected]>
4040
* @copyright Copyright (c) 2010 David Abdemoulaie (http://hobodave.com/)
4141
* @license http://hobodave.com/license.txt New BSD License
42+
* @deprecated
4243
*/
4344
class WhereInWalker extends TreeWalkerAdapter
4445
{

src/Knp/Component/Pager/Event/Subscriber/Paginate/Doctrine/ORM/QuerySubscriber.php

+3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
use Doctrine\ORM\Tools\Pagination\WhereInWalker as DoctrineWhereInWalker;
1515
use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker as DoctrineLimitSubqueryWalker;
1616

17+
/**
18+
* @deprecated see UsesPaginator
19+
**/
1720
class QuerySubscriber implements EventSubscriberInterface
1821
{
1922
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber;
4+
5+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
6+
use Knp\Component\Pager\Event\ItemsEvent;
7+
use Doctrine\ORM\Query;
8+
use Doctrine\ORM\Tools\Pagination\Paginator;
9+
use Doctrine\ORM\Tools\Pagination\CountWalker;
10+
11+
class UsesPaginator implements EventSubscriberInterface
12+
{
13+
const HINT_FETCH_JOIN_COLLECTION = 'knp_paginator.fetch_join_collection';
14+
15+
public function items(ItemsEvent $event)
16+
{
17+
if (!class_exists('Doctrine\ORM\Tools\Pagination\Paginator')) {
18+
return;
19+
}
20+
if (!$event->target instanceof Query) {
21+
return;
22+
}
23+
$event->stopPropagation();
24+
25+
$useOutputWalkers = false;
26+
if (isset($event->options['wrap-queries'])) {
27+
$useOutputWalkers = $event->options['wrap-queries'];
28+
}
29+
30+
$event->target
31+
->setFirstResult($event->getOffset())
32+
->setMaxResults($event->getLimit())
33+
->setHint(CountWalker::HINT_DISTINCT, $event->options['distinct'])
34+
;
35+
36+
$fetchJoinCollection = true;
37+
if ($event->target->hasHint(self::HINT_FETCH_JOIN_COLLECTION)) {
38+
$fetchJoinCollection = $event->target->getHint(self::HINT_FETCH_JOIN_COLLECTION);
39+
}
40+
41+
$paginator = new Paginator($event->target, $fetchJoinCollection);
42+
$paginator->setUseOutputWalkers($useOutputWalkers);
43+
$event->count = count($paginator);
44+
$event->items = iterator_to_array($paginator);
45+
}
46+
47+
public static function getSubscribedEvents()
48+
{
49+
return array(
50+
'knp_pager.items' => array('items', 0)
51+
);
52+
}
53+
}

src/Knp/Component/Pager/Event/Subscriber/Paginate/PaginationSubscriber.php

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public function before(BeforeEvent $event)
2121
// hook all standard subscribers
2222
$disp->addSubscriber(new ArraySubscriber);
2323
$disp->addSubscriber(new Doctrine\ORM\QueryBuilderSubscriber);
24+
$disp->addSubscriber(new Doctrine\ORM\QuerySubscriber\UsesPaginator);
2425
$disp->addSubscriber(new Doctrine\ORM\QuerySubscriber);
2526
$disp->addSubscriber(new Doctrine\ODM\MongoDB\QueryBuilderSubscriber);
2627
$disp->addSubscriber(new Doctrine\ODM\MongoDB\QuerySubscriber);

tests/Test/Pager/Subscriber/Paginate/Doctrine/ODM/MongoDB/GridFsTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ function shouldPaginate()
2929

3030
private function populate()
3131
{
32-
$mockFile = realpath(TESTS_PATH.'/temp/summer.gif');
32+
$mockFile = __DIR__.'/summer.gif';
3333
$dm = $this->getMockDocumentManager();
3434
$summer = new Image;
3535
$summer->setTitle('summer');

tests/Test/Pager/Subscriber/Paginate/Doctrine/ORM/AdvancedQueryTest.php

+22-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function shouldBeAbleToPaginateWithHavingClause()
5555
$q = $this->em->createQuery($dql);
5656
$q->setHydrationMode(Query::HYDRATE_ARRAY);
5757
$p = new Paginator;
58-
$view = $p->paginate($q, 1, 10);
58+
$view = $p->paginate($q, 1, 10, array('wrap-queries' => true));
5959
$this->assertEquals(3, count($view));
6060
}
6161

@@ -126,6 +126,27 @@ function shouldBeAbleToPaginateCaseBasedQuery()
126126
$this->assertEquals(1, $items[0]['relevance']);
127127
}
128128

129+
/**
130+
* @test
131+
*/
132+
function shouldUseOutputWalkersIfHinted()
133+
{
134+
$this->populate();
135+
136+
$dql = <<<___SQL
137+
SELECT p, t
138+
FROM Test\Fixture\Entity\Shop\Product p
139+
INNER JOIN p.tags t
140+
GROUP BY p.id
141+
HAVING p.numTags = COUNT(t)
142+
___SQL;
143+
$q = $this->em->createQuery($dql);
144+
$q->setHydrationMode(Query::HYDRATE_ARRAY);
145+
$p = new Paginator;
146+
$view = $p->paginate($q, 1, 10, array('wrap-queries' => true));
147+
$this->assertEquals(3, count($view));
148+
}
149+
129150
protected function getUsedEntityFixtures()
130151
{
131152
return array(

tests/Test/Pager/Subscriber/Paginate/Doctrine/ORM/CompositeKeyTest.php

+4-14
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,11 @@
55
use Test\Tool\BaseTestCaseORM;
66
use Knp\Component\Pager\Paginator;
77
use Test\Fixture\Entity\Composite;
8+
use Knp\Component\Pager\Event\Subscriber\Paginate\Doctrine\ORM\QuerySubscriber\UsesPaginator;
9+
use Doctrine\ORM\Query;
810

911
class CompositeKeyTest extends BaseTestCaseORM
1012
{
11-
/**
12-
* @test
13-
* @expectedException Doctrine\ORM\Mapping\MappingException
14-
*/
15-
function shouldNotBeAbleToCountCompositeKeyOnByCountQueryWalker()
16-
{
17-
$p = new Paginator;
18-
$em = $this->getMockSqliteEntityManager();
19-
20-
$query = $em->createQuery('SELECT c FROM Test\Fixture\Entity\Composite c');
21-
$view = $p->paginate($query);
22-
}
23-
2413
/**
2514
* @test
2615
*/
@@ -38,7 +27,8 @@ function shouldBeHandledByQueryHintByPassingCount()
3827
->createQuery('SELECT c FROM Test\Fixture\Entity\Composite c')
3928
->setHint('knp_paginator.count', $count)
4029
;
41-
$view = $p->paginate($query);
30+
$query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, false);
31+
$view = $p->paginate($query, 1, 10, array('wrap-queries' => true));
4232

4333
$items = $view->getItems();
4434
$this->assertEquals(0, count($items));

0 commit comments

Comments
 (0)