├── CHANGELOG.md
├── LICENSE
├── README.md
├── code_of_conduct.md
├── composer.json
└── src
└── Pagerfanta
├── Adapter
├── AdapterInterface.php
├── ArrayAdapter.php
├── CallbackAdapter.php
├── ConcatenationAdapter.php
├── DoctrineCollectionAdapter.php
├── DoctrineDbalAdapter.php
├── DoctrineDbalSingleTableAdapter.php
├── DoctrineODMMongoDBAdapter.php
├── DoctrineODMPhpcrAdapter.php
├── DoctrineORMAdapter.php
├── DoctrineSelectableAdapter.php
├── ElasticaAdapter.php
├── FixedAdapter.php
├── MandangoAdapter.php
├── MongoAdapter.php
├── NullAdapter.php
├── Propel2Adapter.php
├── PropelAdapter.php
└── SolariumAdapter.php
├── Exception
├── Exception.php
├── InvalidArgumentException.php
├── LessThan1CurrentPageException.php
├── LessThan1MaxPerPageException.php
├── LogicException.php
├── NotBooleanException.php
├── NotIntegerCurrentPageException.php
├── NotIntegerException.php
├── NotIntegerMaxPerPageException.php
├── NotValidCurrentPageException.php
├── NotValidMaxPerPageException.php
└── OutOfRangeCurrentPageException.php
├── Pagerfanta.php
├── PagerfantaInterface.php
└── View
├── DefaultView.php
├── OptionableView.php
├── SemanticUiView.php
├── Template
├── DefaultTemplate.php
├── SemanticUiTemplate.php
├── Template.php
├── TemplateInterface.php
├── TwitterBootstrap3Template.php
├── TwitterBootstrap4Template.php
└── TwitterBootstrapTemplate.php
├── TwitterBootstrap3View.php
├── TwitterBootstrap4View.php
├── TwitterBootstrapView.php
├── ViewFactory.php
├── ViewFactoryInterface.php
└── ViewInterface.php
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ### 1.0.2 (-)
2 |
3 | * Added `DoctrineODMPhpcrAdapter`
4 | * Added endpoint to `SolariumAdapter`
5 | * Added $useOutputWalkers mode into the DoctrineORM adapter
6 |
7 | ### 1.0.1 (2013-09-23)
8 |
9 | * Added `TwitterBootstrap3View`
10 | * Made `getResultSet` public in the `SolariumAdapter`
11 | * Fixed the `last` method in the `DefaultView` to call the last method of the template
12 | * Added `currentPageOffsetStart` and `currentPageOffsetEnd` to `Pagerfanta`
13 | * Fixed the minimum number of pages to 1
14 |
15 | ### 1.0.0 (2013-04-24)
16 |
17 | * Initial release
18 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011 White October Ltd
2 | http://www.whiteoctober.co.uk/
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is furnished
9 | to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **NB** This project is no longer maintained; you may like to use https://github.com/BabDev/Pagerfanta instead.
2 |
3 | # Pagerfanta
4 |
5 | [](https://travis-ci.org/whiteoctober/Pagerfanta) [](https://scrutinizer-ci.com/g/whiteoctober/Pagerfanta/) [](https://scrutinizer-ci.com/g/whiteoctober/Pagerfanta/) [](https://insight.sensiolabs.com/projects/9e710230-b088-4904-baef-5f5e2d62e681) [](https://packagist.org/packages/pagerfanta/pagerfanta) [](https://packagist.org/packages/pagerfanta/pagerfanta)
6 |
7 | This project is for PHP 7.
8 | If you need support for PHP < 7, use [Release v1.1.0](https://github.com/whiteoctober/Pagerfanta/releases/tag/v1.1.0).
9 |
10 | ## Usage
11 |
12 | ```php
13 | setMaxPerPage($maxPerPage); // 10 by default
22 | $maxPerPage = $pagerfanta->getMaxPerPage();
23 |
24 | $pagerfanta->setCurrentPage($currentPage); // 1 by default
25 | $currentPage = $pagerfanta->getCurrentPage();
26 |
27 | $nbResults = $pagerfanta->getNbResults();
28 | $currentPageResults = $pagerfanta->getCurrentPageResults();
29 | ```
30 |
31 | Some of the other methods available:
32 |
33 | ```php
34 | $pagerfanta->getNbPages();
35 | $pagerfanta->haveToPaginate(); // whether the number of results is higher than the max per page
36 | $pagerfanta->hasPreviousPage();
37 | $pagerfanta->getPreviousPage();
38 | $pagerfanta->hasNextPage();
39 | $pagerfanta->getNextPage();
40 | $pagerfanta->getCurrentPageOffsetStart();
41 | $pagerfanta->getCurrentPageOffsetEnd();
42 | ```
43 |
44 | ### Changing the page based on user selection
45 |
46 | If you're using the example route-generator function shown below,
47 | the page selected by the user will be available in the `page` GET (querystring) parameter.
48 |
49 | You would then need to call `setCurrentPage` with the value of that parameter:
50 |
51 | ```php
52 | if (isset($_GET["page"])) {
53 | $pagerfanta->setCurrentPage($_GET["page"]);
54 | }
55 | ```
56 |
57 | ### setMaxPerPage and setCurrentPage
58 |
59 | The `->setMaxPerPage()` and `->setCurrentPage()` methods implement
60 | a fluent interface:
61 |
62 | ```php
63 | setMaxPerPage($maxPerPage)
67 | ->setCurrentPage($currentPage);
68 | ```
69 |
70 | The `->setMaxPerPage()` method throws an exception if the max per page
71 | is not valid:
72 |
73 | * `Pagerfanta\Exception\NotIntegerMaxPerPageException`
74 | * `Pagerfanta\Exception\LessThan1MaxPerPageException`
75 |
76 | Both extend from `Pagerfanta\Exception\NotValidMaxPerPageException`.
77 |
78 | The `->setCurrentPage()` method throws an exception if the page is not valid:
79 |
80 | * `Pagerfanta\Exception\NotIntegerCurrentPageException`
81 | * `Pagerfanta\Exception\LessThan1CurrentPageException`
82 | * `Pagerfanta\Exception\OutOfRangeCurrentPageException`
83 |
84 | All of them extend from `Pagerfanta\Exception\NotValidCurrentPageException`.
85 |
86 | `->setCurrentPage()` throws an out ot range exception depending on the
87 | max per page, so if you are going to modify the max per page, you should do it
88 | before setting the current page.
89 |
90 | (If you want to use Pagerfanta in a Symfony project, see
91 | [https://github.com/whiteoctober/WhiteOctoberPagerfantaBundle](https://github.com/whiteoctober/WhiteOctoberPagerfantaBundle).)
92 |
93 | ## Adapters
94 |
95 | The adapter's concept is very simple. An adapter just returns the number
96 | of results and an slice for a offset and length. This way you can adapt
97 | a pagerfanta to paginate any kind results simply by creating an adapter.
98 |
99 | An adapter must implement the `Pagerfanta\Adapter\AdapterInterface`
100 | interface, which has these two methods:
101 |
102 | ```php
103 | find();
147 | $adapter = new MongoAdapter($cursor);
148 | ```
149 |
150 | ### MandangoAdapter
151 |
152 | To paginate [Mandango](http://mandango.org) Queries.
153 |
154 | ```php
155 | getRepository('Model\Article')->createQuery();
160 | $adapter = new MandangoAdapter($query);
161 | ```
162 |
163 | ### DoctrineDbalAdapter
164 |
165 | To paginate [DoctrineDbal](http://www.doctrine-project.org/projects/dbal.html)
166 | query builders.
167 |
168 | ```php
169 | select('p.*')->from('posts', 'p');
176 |
177 | $countQueryBuilderModifier = function ($queryBuilder) {
178 | $queryBuilder->select('COUNT(DISTINCT p.id) AS total_results')
179 | ->setMaxResults(1);
180 | };
181 |
182 | $adapter = new DoctrineDbalAdapter($queryBuilder, $countQueryBuilderModifier);
183 | ```
184 |
185 | ### DoctrineDbalSingleTableAdapter
186 |
187 | To simplify the pagination of single table
188 | [DoctrineDbal](http://www.doctrine-project.org/projects/dbal.html)
189 | query builders.
190 |
191 | This adapter only paginates single table query builders, without joins.
192 |
193 | ```php
194 | select('p.*')->from('posts', 'p');
201 |
202 | $countField = 'p.id';
203 |
204 | $adapter = new DoctrineDbalSingleTableAdapter($queryBuilder, $countField);
205 | ```
206 |
207 | ### DoctrineORMAdapter
208 |
209 | To paginate [DoctrineORM](http://www.doctrine-project.org/projects/orm) query objects.
210 |
211 | ```php
212 | createQueryBuilder()
217 | ->select('u')
218 | ->from('Model\Article', 'u');
219 | $adapter = new DoctrineORMAdapter($queryBuilder);
220 | ```
221 |
222 | ### DoctrineODMMongoDBAdapter
223 |
224 | To paginate [DoctrineODMMongoDB](http://www.doctrine-project.org/docs/mongodb_odm/1.0/en/) query builders.
225 |
226 | ```php
227 | createQueryBuilder('Model\Article');
232 | $adapter = new DoctrineODMMongoDBAdapter($queryBuilder);
233 | ```
234 |
235 | ### DoctrineODMPhpcrAdapter
236 |
237 | To paginate [Doctrine PHPCR-ODM](http://docs.doctrine-project.org/projects/doctrine-phpcr-odm/en/latest/) query builders.
238 |
239 | ```php
240 | createQueryBuilder();
245 | $queryBuilder->from('Model\Article');
246 | $adapter = new DoctrineODMPhpcrAdapter($queryBuilder);
247 | ```
248 |
249 | ### DoctrineCollectionAdapter
250 |
251 | To paginate a `Doctrine\Common\Collection\Collections` interface
252 | you can use the `DoctrineCollectionAdapter`. It proxies to the
253 | count() and slice() methods on the Collections interface for
254 | pagination. This makes sense if you are using Doctrine ORMs Extra
255 | Lazy association features:
256 |
257 | ```php
258 | find("Pagerfanta\Tests\Adapter\DoctrineORM\User", 1);
263 |
264 | $adapter = new DoctrineCollectionAdapter($user->getGroups());
265 | ```
266 |
267 | ### DoctrineSelectableAdapter
268 |
269 | To paginate a `Doctrine\Common\Collection\Selectable` interface
270 | you can use the `DoctrineSelectableAdapter`. It uses the matching()
271 | method on the Selectable interface for pagination. This is
272 | especially usefull when using the Doctrine Criteria object to
273 | filter a PersistentCollection:
274 |
275 | ```php
276 | find("Pagerfanta\Tests\Adapter\DoctrineORM\User", 1);
282 | $comments = $user->getComments();
283 | $criteria = Criteria::create()->andWhere(Criteria::expr()->in('id', array(1,2,3));
284 |
285 | $adapter = new DoctrineSelectableAdapter($comments, $criteria);
286 | ```
287 |
288 | Note that you should never use this adapter with a
289 | PersistentCollection which is not set to use the EXTRA_LAZY fetch mode.
290 |
291 | *Be careful when using the `count()` method, currently Doctrine2
292 | needs to fetch all the records to count the number of elements.*
293 |
294 | ### ElasticaAdapter
295 |
296 | To paginate an Elastica Query query:
297 |
298 | ```php
299 | 'Fred'
311 | ));
312 |
313 | $adapter = new ElasticaAdapter($searchable, $query);
314 | ```
315 |
316 | *Be careful when paginating a huge set of documents. By default, offset + limit
317 | can't exceed 10000. You can mitigate this by setting the `$maxResults`
318 | parameter when constructing the `ElasticaAdapter`. For more information, see:
319 | [#213](https://github.com/whiteoctober/Pagerfanta/pull/213#issue-87631892).*
320 |
321 | ### PropelAdapter
322 |
323 | To paginate a propel 1 query:
324 |
325 | ```php
326 | createSelect();
355 | $query->setQuery('search term');
356 |
357 | $adapter = new SolariumAdapter($solarium, $query);
358 | ```
359 |
360 | ### FixedAdapter
361 |
362 | Best used when you need to do a custom paging solution and
363 | don't want to implement a full adapter for a one-off use case.
364 |
365 | It returns always the same data no matter what page you query:
366 |
367 | ```php
368 | 3);
448 | $html = $view->render($pagerfanta, $routeGenerator, $options);
449 | ```
450 |
451 | Options (default):
452 |
453 | * proximity (3)
454 | * prev_message (Previous)
455 | * next_message (Next)
456 | * css_disabled_class (disabled)
457 | * css_dots_class (dots)
458 | * css_current_class (current)
459 | * dots_text (...)
460 | * container_template ()
461 | * page_template (%text%)
462 | * span_template (%text%)
463 |
464 | CSS:
465 |
466 | ```css
467 | .pagerfanta {
468 | }
469 |
470 | .pagerfanta a,
471 | .pagerfanta span {
472 | display: inline-block;
473 | border: 1px solid blue;
474 | color: blue;
475 | margin-right: .2em;
476 | padding: .25em .35em;
477 | }
478 |
479 | .pagerfanta a {
480 | text-decoration: none;
481 | }
482 |
483 | .pagerfanta a:hover {
484 | background: #ccf;
485 | }
486 |
487 | .pagerfanta .dots {
488 | border-width: 0;
489 | }
490 |
491 | .pagerfanta .current {
492 | background: #ccf;
493 | font-weight: bold;
494 | }
495 |
496 | .pagerfanta .disabled {
497 | border-color: #ccf;
498 | color: #ccf;
499 | }
500 |
501 | COLORS:
502 |
503 | .pagerfanta a,
504 | .pagerfanta span {
505 | border-color: blue;
506 | color: blue;
507 | }
508 |
509 | .pagerfanta a:hover {
510 | background: #ccf;
511 | }
512 |
513 | .pagerfanta .current {
514 | background: #ccf;
515 | }
516 |
517 | .pagerfanta .disabled {
518 | border-color: #ccf;
519 | color: #cf;
520 | }
521 | ```
522 |
523 | ### TwitterBootstrapView, TwitterBootstrap3View and TwitterBootstrap4View
524 |
525 | These views generate paginators designed for use with
526 | [Twitter Bootstrap](https://github.com/twitter/bootstrap).
527 |
528 | `TwitterBootstrapView` is for Bootstrap 2; `TwitterBootstrap3View` is for Bootstrap 3; `TwitterBootstrap4View` is for Bootstrap 4 (alpha).
529 |
530 | ```php
531 | 3);
537 | $html = $view->render($pagerfanta, $routeGenerator, $options);
538 | ```
539 |
540 | Options (default):
541 |
542 | * proximity (3)
543 | * prev_message (← Previous)
544 | * prev_disabled_href ()
545 | * next_message (Next →)
546 | * next_disabled_href ()
547 | * dots_message (…)
548 | * dots_href ()
549 | * css_container_class (pagination)
550 | * css_prev_class (prev)
551 | * css_next_class (next)
552 | * css_disabled_class (disabled)
553 | * css_dots_class (disabled)
554 | * css_active_class (active)
555 |
556 | ### SemanticUiView
557 |
558 | This view generates a pagination for
559 | [Semantic UI](https://github.com/Semantic-Org/Semantic-UI).
560 |
561 | ```php
562 | 3);
568 | $html = $view->render($pagerfanta, $routeGenerator, $options);
569 | ```
570 |
571 | Options (default):
572 |
573 | * proximity (3)
574 | * prev_message (← Previous)
575 | * prev_disabled_href ()
576 | * next_message (Next →)
577 | * next_disabled_href ()
578 | * dots_message (…)
579 | * dots_href ()
580 | * css_container_class (pagination)
581 | * css_item_class (item)
582 | * css_prev_class (prev)
583 | * css_next_class (next)
584 | * css_disabled_class (disabled)
585 | * css_dots_class (disabled)
586 | * css_active_class (active)
587 |
588 | ### OptionableView
589 |
590 | This view is to reuse options in different views.
591 |
592 | ```php
593 | 3));
602 |
603 | $myView2 = new OptionableView($defaultView, array('prev_message' => 'Anterior', 'next_message' => 'Siguiente'));
604 |
605 | // using in a normal way
606 | $pagerfantaHtml = $myView2->render($pagerfanta, $routeGenerator);
607 |
608 | // overwriting default options
609 | $pagerfantaHtml = $myView2->render($pagerfanta, $routeGenerator, array('next_message' => 'Siguiente!!'));
610 | ```
611 |
612 | ## Contributing
613 |
614 | We welcome contributions to this project, including pull requests and issues (and discussions on existing issues).
615 |
616 | If you'd like to contribute code but aren't sure what, the [issues list](https://github.com/whiteoctober/pagerfanta/issues) is a good place to start.
617 | If you're a first-time code contributor, you may find Github's guide to [forking projects](https://guides.github.com/activities/forking/) helpful.
618 |
619 | All contributors (whether contributing code, involved in issue discussions, or involved in any other way) must abide by our [code of conduct](code_of_conduct.md).
620 |
621 | ## Acknowledgements
622 |
623 | Pagerfanta is inspired by [Zend Paginator](https://github.com/zendframework/zf2).
624 |
625 | Thanks also to Pablo Díez (pablodip@gmail.com) for most of the work on the first versions of Pagerfanta.
626 |
627 | ## Licence
628 |
629 | Pagerfanta is licensed under the [MIT License](LICENSE).
630 |
--------------------------------------------------------------------------------
/code_of_conduct.md:
--------------------------------------------------------------------------------
1 | This project's code-of-conduct can be found at [https://github.com/whiteoctober/open-source-code-of-conduct/blob/master/code_of_conduct.md](https://github.com/whiteoctober/open-source-code-of-conduct/blob/master/code_of_conduct.md).
2 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pagerfanta/pagerfanta",
3 | "description": "Pagination for PHP",
4 | "keywords": ["page","paging", "paginator", "pagination"],
5 | "type": "library",
6 | "license": "MIT",
7 | "authors": [
8 | {
9 | "name": "Pablo Díez",
10 | "email": "pablodip@gmail.com"
11 | }
12 | ],
13 | "require": {
14 | "php": "^7.0"
15 | },
16 | "require-dev": {
17 | "phpunit/phpunit": "^6.5",
18 | "doctrine/orm": "~2.3",
19 | "mandango/mandango": "~1.0@dev",
20 | "mandango/mondator": "~1.0@dev",
21 | "jmikola/geojson": "~1.0",
22 | "doctrine/phpcr-odm": "1.*",
23 | "propel/propel": "~2.0@dev",
24 | "propel/propel1": "~1.6",
25 | "ruflin/Elastica": "~1.3",
26 | "solarium/solarium": "~3.1",
27 | "jackalope/jackalope-doctrine-dbal": "1.*"
28 | },
29 | "suggest": {
30 | "doctrine/orm": "To use the DoctrineORMAdapter.",
31 | "mandango/mandango": "To use the MandangoAdapter.",
32 | "doctrine/mongodb-odm": "To use the DoctrineODMMongoDBAdapter.",
33 | "doctrine/phpcr-odm": "To use the DoctrineODMPhpcrAdapter. >= 1.1.0",
34 | "propel/propel1": "To use the PropelAdapter",
35 | "propel/propel": "To use the Propel2Adapter",
36 | "solarium/solarium": "To use the SolariumAdapter."
37 | },
38 | "autoload": {
39 | "psr-4": { "Pagerfanta\\": "src/Pagerfanta/" }
40 | },
41 | "autoload-dev": {
42 | "psr-4": { "Pagerfanta\\Tests\\": "tests/Pagerfanta/Tests/" }
43 | },
44 | "extra": {
45 | "branch-alias": {
46 | "dev-master": "1.0.x-dev"
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/AdapterInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | /**
15 | * AdapterInterface.
16 | *
17 | * @author Pablo Díez
18 | */
19 | interface AdapterInterface
20 | {
21 | /**
22 | * Returns the number of results.
23 | *
24 | * @return integer The number of results.
25 | */
26 | public function getNbResults();
27 |
28 | /**
29 | * Returns an slice of the results.
30 | *
31 | * @param integer $offset The offset.
32 | * @param integer $length The length.
33 | *
34 | * @return array|\Traversable The slice.
35 | */
36 | public function getSlice($offset, $length);
37 | }
38 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/ArrayAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | /**
15 | * ArrayAdapter.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class ArrayAdapter implements AdapterInterface
20 | {
21 | private $array;
22 |
23 | /**
24 | * Constructor.
25 | *
26 | * @param array $array The array.
27 | */
28 | public function __construct(array $array)
29 | {
30 | $this->array = $array;
31 | }
32 |
33 | /**
34 | * Returns the array.
35 | *
36 | * @return array The array.
37 | */
38 | public function getArray()
39 | {
40 | return $this->array;
41 | }
42 |
43 | /**
44 | * {@inheritdoc}
45 | */
46 | public function getNbResults()
47 | {
48 | return count($this->array);
49 | }
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | public function getSlice($offset, $length)
55 | {
56 | return array_slice($this->array, $offset, $length);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/CallbackAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Pagerfanta\Exception\InvalidArgumentException;
15 |
16 | /**
17 | * @author Adrien Brault
18 | */
19 | class CallbackAdapter implements AdapterInterface
20 | {
21 | private $getNbResultsCallback;
22 | private $getSliceCallback;
23 |
24 | /**
25 | * @param callable $getNbResultsCallback
26 | * @param callable $getSliceCallback
27 | */
28 | public function __construct($getNbResultsCallback, $getSliceCallback)
29 | {
30 | if (!is_callable($getNbResultsCallback)) {
31 | throw new InvalidArgumentException('$getNbResultsCallback should be a callable');
32 | }
33 | if (!is_callable($getSliceCallback)) {
34 | throw new InvalidArgumentException('$getSliceCallback should be a callable');
35 | }
36 |
37 | $this->getNbResultsCallback = $getNbResultsCallback;
38 | $this->getSliceCallback = $getSliceCallback;
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function getNbResults()
45 | {
46 | return call_user_func($this->getNbResultsCallback);
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | */
52 | public function getSlice($offset, $length)
53 | {
54 | return call_user_func($this->getSliceCallback, $offset, $length);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/ConcatenationAdapter.php:
--------------------------------------------------------------------------------
1 |
11 | */
12 | class ConcatenationAdapter implements AdapterInterface
13 | {
14 | /**
15 | * @var AdapterInterface[] List of adapters
16 | */
17 | protected $adapters;
18 |
19 | /**
20 | * @var int[]|null Cache of the numbers of results of the adapters. The indexes correspond the indexes of the
21 | * `adapters` property.
22 | */
23 | protected $adaptersNbResultsCache;
24 |
25 | /**
26 | * @param AdapterInterface[] $adapters
27 | * @throws InvalidArgumentException
28 | */
29 | public function __construct(array $adapters)
30 | {
31 | foreach ($adapters as $index => $adapter) {
32 | if (!($adapter instanceof AdapterInterface)) {
33 | throw new InvalidArgumentException(sprintf(
34 | 'Argument $adapters[%s] expected to be a \Pagerfanta\Adapter\AdapterInterface instance, a %s given',
35 | $index,
36 | is_object($adapter) ? sprintf('%s instance', get_class($adapter)) : gettype($adapter)
37 | ));
38 | }
39 | }
40 |
41 | $this->adapters = $adapters;
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | public function getNbResults()
48 | {
49 | if (!isset($this->adaptersNbResultsCache)) {
50 | $this->refreshAdaptersNbResults();
51 | }
52 |
53 | return array_sum($this->adaptersNbResultsCache);
54 | }
55 |
56 | /**
57 | * {@inheritdoc}
58 | * @return array
59 | */
60 | public function getSlice($offset, $length)
61 | {
62 | if (!isset($this->adaptersNbResultsCache)) {
63 | $this->refreshAdaptersNbResults();
64 | }
65 |
66 | $slice = array();
67 | $previousAdaptersNbResultsSum = 0;
68 | $requestFirstIndex = $offset;
69 | $requestLastIndex = $offset + $length - 1;
70 |
71 | foreach ($this->adapters as $index => $adapter) {
72 | $adapterNbResults = $this->adaptersNbResultsCache[$index];
73 | $adapterFirstIndex = $previousAdaptersNbResultsSum;
74 | $adapterLastIndex = $adapterFirstIndex + $adapterNbResults - 1;
75 |
76 | $previousAdaptersNbResultsSum += $adapterNbResults;
77 |
78 | // The adapter is fully below the requested slice range — skip it
79 | if ($adapterLastIndex < $requestFirstIndex) {
80 | continue;
81 | }
82 |
83 | // The adapter is fully above the requested slice range — finish the gathering
84 | if ($adapterFirstIndex > $requestLastIndex) {
85 | break;
86 | }
87 |
88 | // Else the adapter range definitely intersects with the requested range
89 | $fetchOffset = $requestFirstIndex - $adapterFirstIndex;
90 | $fetchLength = $length;
91 |
92 | // The requested range start is below the adapter range start
93 | if ($fetchOffset < 0) {
94 | $fetchLength += $fetchOffset;
95 | $fetchOffset = 0;
96 | }
97 |
98 | // The requested range end is above the adapter range end
99 | if ($fetchOffset + $fetchLength > $adapterNbResults) {
100 | $fetchLength = $adapterNbResults - $fetchOffset;
101 | }
102 |
103 | // Getting the subslice from the adapter and adding it to the result slice
104 | $fetchSlice = $adapter->getSlice($fetchOffset, $fetchLength);
105 | foreach ($fetchSlice as $item) {
106 | $slice[] = $item;
107 | }
108 | }
109 |
110 | return $slice;
111 | }
112 |
113 | /**
114 | * Refreshes the cache of the numbers of results of the adapters.
115 | */
116 | protected function refreshAdaptersNbResults()
117 | {
118 | if (!isset($this->adaptersNbResultsCache)) {
119 | $this->adaptersNbResultsCache = array();
120 | }
121 |
122 | foreach ($this->adapters as $index => $adapter) {
123 | $this->adaptersNbResultsCache[$index] = $adapter->getNbResults();
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/DoctrineCollectionAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Doctrine\Common\Collections\Collection;
15 |
16 | /**
17 | * DoctrineCollectionAdapter.
18 | *
19 | * @author Pablo Díez
20 | */
21 | class DoctrineCollectionAdapter implements AdapterInterface
22 | {
23 | private $collection;
24 |
25 | /**
26 | * Constructor.
27 | *
28 | * @param Collection $collection A Doctrine collection.
29 | */
30 | public function __construct(Collection $collection)
31 | {
32 | $this->collection = $collection;
33 | }
34 |
35 | /**
36 | * Returns the collection.
37 | *
38 | * @return Collection The collection.
39 | */
40 | public function getCollection()
41 | {
42 | return $this->collection;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function getNbResults()
49 | {
50 | return $this->collection->count();
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function getSlice($offset, $length)
57 | {
58 | return $this->collection->slice($offset, $length);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/DoctrineDbalAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Doctrine\DBAL\Query\QueryBuilder;
15 | use Pagerfanta\Exception\InvalidArgumentException;
16 |
17 | /**
18 | * @author Michael Williams
19 | * @author Pablo Díez
20 | */
21 | class DoctrineDbalAdapter implements AdapterInterface
22 | {
23 | private $queryBuilder;
24 | private $countQueryBuilderModifier;
25 |
26 | /**
27 | * Constructor.
28 | *
29 | * @param QueryBuilder $queryBuilder A DBAL query builder.
30 | * @param callable $countQueryBuilderModifier A callable to modifier the query builder to count.
31 | */
32 | public function __construct(QueryBuilder $queryBuilder, $countQueryBuilderModifier)
33 | {
34 | if ($queryBuilder->getType() !== QueryBuilder::SELECT) {
35 | throw new InvalidArgumentException('Only SELECT queries can be paginated.');
36 | }
37 |
38 | if (!is_callable($countQueryBuilderModifier)) {
39 | throw new InvalidArgumentException('The count query builder modifier must be a callable.');
40 | }
41 |
42 | $this->queryBuilder = clone $queryBuilder;
43 | $this->countQueryBuilderModifier = $countQueryBuilderModifier;
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | public function getNbResults()
50 | {
51 | $qb = $this->prepareCountQueryBuilder();
52 | $result = $qb->execute()->fetchColumn();
53 |
54 | return (int) $result;
55 | }
56 |
57 | private function prepareCountQueryBuilder()
58 | {
59 | $qb = clone $this->queryBuilder;
60 | call_user_func($this->countQueryBuilderModifier, $qb);
61 |
62 | return $qb;
63 | }
64 |
65 | /**
66 | * {@inheritdoc}
67 | */
68 | public function getSlice($offset, $length)
69 | {
70 | $qb = clone $this->queryBuilder;
71 | $result = $qb->setMaxResults($length)
72 | ->setFirstResult($offset)
73 | ->execute();
74 |
75 | return $result->fetchAll();
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/DoctrineDbalSingleTableAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Doctrine\DBAL\Query\QueryBuilder;
15 | use Pagerfanta\Exception\InvalidArgumentException;
16 |
17 | /**
18 | * @author Michael Williams
19 | * @author Pablo Díez
20 | */
21 | class DoctrineDbalSingleTableAdapter extends DoctrineDbalAdapter
22 | {
23 | /**
24 | * Constructor.
25 | *
26 | * @param QueryBuilder $queryBuilder A DBAL query builder.
27 | * @param string $countField Primary key for the table in query. Used in count expression. Must include table alias
28 | */
29 | public function __construct(QueryBuilder $queryBuilder, $countField)
30 | {
31 | if ($this->hasQueryBuilderJoins($queryBuilder)) {
32 | throw new InvalidArgumentException('The query builder cannot have joins.');
33 | }
34 |
35 | $countQueryBuilderModifier = $this->createCountQueryModifier($countField);
36 |
37 | parent::__construct($queryBuilder, $countQueryBuilderModifier);
38 | }
39 |
40 | private function hasQueryBuilderJoins(QueryBuilder $queryBuilder)
41 | {
42 | $joins = $queryBuilder->getQueryPart('join');
43 |
44 | return !empty($joins);
45 | }
46 |
47 | private function createCountQueryModifier($countField)
48 | {
49 | $select = $this->createSelectForCountField($countField);
50 |
51 | return function (QueryBuilder $queryBuilder) use ($select) {
52 | $queryBuilder->select($select)
53 | ->resetQueryPart('orderBy')
54 | ->setMaxResults(1);
55 | };
56 | }
57 |
58 | private function createSelectForCountField($countField)
59 | {
60 | if ($this->countFieldHasNoAlias($countField)) {
61 | throw new InvalidArgumentException('The $countField must contain a table alias in the string.');
62 | }
63 |
64 | return sprintf('COUNT(DISTINCT %s) AS total_results', $countField);
65 | }
66 |
67 | private function countFieldHasNoAlias($countField)
68 | {
69 | return strpos($countField, '.') === false;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/DoctrineODMMongoDBAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Doctrine\ODM\MongoDB\Query\Builder;
15 |
16 | /**
17 | * DoctrineODMMongoDBAdapter.
18 | *
19 | * @author Pablo Díez
20 | */
21 | class DoctrineODMMongoDBAdapter implements AdapterInterface
22 | {
23 | private $queryBuilder;
24 |
25 | /**
26 | * Constructor.
27 | *
28 | * @param Builder $queryBuilder A DoctrineMongo query builder.
29 | */
30 | public function __construct(Builder $queryBuilder)
31 | {
32 | $this->queryBuilder = $queryBuilder;
33 | }
34 |
35 | /**
36 | * Returns the query builder.
37 | *
38 | * @return Builder The query builder.
39 | */
40 | public function getQueryBuilder()
41 | {
42 | return $this->queryBuilder;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function getNbResults()
49 | {
50 | $qb = clone $this->queryBuilder;
51 |
52 | return $qb
53 | ->limit(0)
54 | ->skip(0)
55 | ->count()
56 | ->getQuery()
57 | ->execute();
58 | }
59 |
60 | /**
61 | * {@inheritdoc}
62 | */
63 | public function getSlice($offset, $length)
64 | {
65 | return $this->queryBuilder
66 | ->limit($length)
67 | ->skip($offset)
68 | ->getQuery()
69 | ->execute();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/DoctrineODMPhpcrAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Doctrine\ODM\PHPCR\Query\Builder\QueryBuilder;
15 | use Doctrine\ODM\PHPCR\Query\Query;
16 |
17 | /**
18 | * Pagerfanta adapter for Doctrine PHPCR-ODM.
19 | *
20 | * @author David Buchmann
21 | */
22 | class DoctrineODMPhpcrAdapter implements AdapterInterface
23 | {
24 | private $queryBuilder;
25 |
26 | /**
27 | * Constructor.
28 | *
29 | * @param QueryBuilder $queryBuilder A Doctrine PHPCR-ODM query builder.
30 | */
31 | public function __construct(QueryBuilder $queryBuilder)
32 | {
33 | $this->queryBuilder = $queryBuilder;
34 | }
35 |
36 | /**
37 | * Returns the query builder.
38 | *
39 | * @return QueryBuilder The query builder.
40 | */
41 | public function getQueryBuilder()
42 | {
43 | return $this->queryBuilder;
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | public function getNbResults()
50 | {
51 | return $this->queryBuilder->getQuery()->execute(null, Query::HYDRATE_PHPCR)->getRows()->count();
52 | }
53 |
54 | /**
55 | * {@inheritdoc}
56 | */
57 | public function getSlice($offset, $length)
58 | {
59 | return $this->queryBuilder
60 | ->getQuery()
61 | ->setMaxResults($length)
62 | ->setFirstResult($offset)
63 | ->execute();
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/DoctrineORMAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
15 |
16 | /**
17 | * DoctrineORMAdapter.
18 | *
19 | * @author Christophe Coevoet
20 | */
21 | class DoctrineORMAdapter implements AdapterInterface
22 | {
23 | /**
24 | * @var \Doctrine\ORM\Tools\Pagination\Paginator
25 | */
26 | private $paginator;
27 |
28 | /**
29 | * Constructor.
30 | *
31 | * @param \Doctrine\ORM\Query|\Doctrine\ORM\QueryBuilder $query A Doctrine ORM query or query builder.
32 | * @param boolean $fetchJoinCollection Whether the query joins a collection (true by default).
33 | * @param boolean|null $useOutputWalkers Whether to use output walkers pagination mode
34 | */
35 | public function __construct($query, $fetchJoinCollection = true, $useOutputWalkers = null)
36 | {
37 | $this->paginator = new DoctrinePaginator($query, $fetchJoinCollection);
38 | $this->paginator->setUseOutputWalkers($useOutputWalkers);
39 | }
40 |
41 | /**
42 | * Returns the query
43 | *
44 | * @return \Doctrine\ORM\Query
45 | */
46 | public function getQuery()
47 | {
48 | return $this->paginator->getQuery();
49 | }
50 |
51 | /**
52 | * Returns whether the query joins a collection.
53 | *
54 | * @return boolean Whether the query joins a collection.
55 | */
56 | public function getFetchJoinCollection()
57 | {
58 | return $this->paginator->getFetchJoinCollection();
59 | }
60 |
61 | /**
62 | * {@inheritdoc}
63 | */
64 | public function getNbResults()
65 | {
66 | return count($this->paginator);
67 | }
68 |
69 | /**
70 | * {@inheritdoc}
71 | */
72 | public function getSlice($offset, $length)
73 | {
74 | $this->paginator
75 | ->getQuery()
76 | ->setFirstResult($offset)
77 | ->setMaxResults($length);
78 |
79 | return $this->paginator->getIterator();
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/DoctrineSelectableAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Doctrine\Common\Collections\Criteria;
15 | use Doctrine\Common\Collections\Selectable;
16 |
17 | /**
18 | * DoctrineSelectableAdapter.
19 | *
20 | * @author Boris Guéry
21 | */
22 | class DoctrineSelectableAdapter implements AdapterInterface
23 | {
24 | /**
25 | * @var Selectable
26 | */
27 | private $selectable;
28 |
29 | /**
30 | * @var Criteria
31 | */
32 | private $criteria;
33 |
34 | /**
35 | * Constructor.
36 | *
37 | * @param Selectable $selectable An implementation of the Selectable interface.
38 | * @param Criteria $criteria A Doctrine criteria.
39 | */
40 | public function __construct(Selectable $selectable, Criteria $criteria)
41 | {
42 | $this->selectable = $selectable;
43 | $this->criteria = $criteria;
44 | }
45 |
46 | /**
47 | * {@inheritdoc}
48 | */
49 | public function getNbResults()
50 | {
51 | $firstResult = null;
52 | $maxResults = null;
53 |
54 | $criteria = $this->createCriteria($firstResult, $maxResults);
55 |
56 | return $this->selectable->matching($criteria)->count();
57 | }
58 |
59 | /**
60 | * {@inheritdoc}
61 | */
62 | public function getSlice($offset, $length)
63 | {
64 | $firstResult = $offset;
65 | $maxResults = $length;
66 |
67 | $criteria = $this->createCriteria($firstResult, $maxResults);
68 |
69 | return $this->selectable->matching($criteria);
70 | }
71 |
72 | private function createCriteria($firstResult, $maxResult)
73 | {
74 | $criteria = clone $this->criteria;
75 | $criteria->setFirstResult($firstResult);
76 | $criteria->setMaxResults($maxResult);
77 |
78 | return $criteria;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/ElasticaAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Elastica\Query;
15 | use Elastica\SearchableInterface;
16 |
17 | class ElasticaAdapter implements AdapterInterface
18 | {
19 | /**
20 | * @var Query
21 | */
22 | private $query;
23 |
24 | /**
25 | * @var \Elastica\ResultSet
26 | */
27 | private $resultSet;
28 |
29 | /**
30 | * @var SearchableInterface
31 | */
32 | private $searchable;
33 |
34 | /**
35 | * @var array
36 | */
37 | private $options;
38 |
39 | /**
40 | * @var int|null
41 | *
42 | * Used to limit the number of totalHits returned by ES.
43 | * For more information, see: https://github.com/whiteoctober/Pagerfanta/pull/213#issue-87631892
44 | */
45 | private $maxResults;
46 |
47 | public function __construct(SearchableInterface $searchable, Query $query, array $options = array(), $maxResults = null)
48 | {
49 | $this->searchable = $searchable;
50 | $this->query = $query;
51 | $this->options = $options;
52 | $this->maxResults = $maxResults;
53 | }
54 |
55 | /**
56 | * Returns the number of results.
57 | *
58 | * @return integer The number of results.
59 | */
60 | public function getNbResults()
61 | {
62 | if (!$this->resultSet) {
63 | $totalHits = $this->searchable->count($this->query);
64 | } else {
65 | $totalHits = $this->resultSet->getTotalHits();
66 | }
67 |
68 | if (null === $this->maxResults) {
69 | return $totalHits;
70 | }
71 |
72 | return min($totalHits, $this->maxResults);
73 | }
74 |
75 | /**
76 | * Returns the Elastica ResultSet. Will return null if getSlice has not yet been
77 | * called.
78 | *
79 | * @return \Elastica\ResultSet|null
80 | */
81 | public function getResultSet()
82 | {
83 | return $this->resultSet;
84 | }
85 |
86 | /**
87 | * Returns an slice of the results.
88 | *
89 | * @param integer $offset The offset.
90 | * @param integer $length The length.
91 | *
92 | * @return array|\Traversable The slice.
93 | */
94 | public function getSlice($offset, $length)
95 | {
96 | return $this->resultSet = $this->searchable->search($this->query, array_merge($this->options, array(
97 | 'from' => $offset,
98 | 'size' => $length
99 | )));
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/FixedAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | /**
15 | * Provides you with an adapter that returns always the same data.
16 | *
17 | * Best used when you need to do a custom paging solution and don't
18 | * want to implement a full adapter for a one-off use case.
19 | *
20 | * @author Jordi Boggiano
21 | */
22 | class FixedAdapter implements AdapterInterface
23 | {
24 | private $nbResults;
25 | private $results;
26 |
27 | /**
28 | * @param int $nbResults
29 | * @param array|\Traversable $results
30 | */
31 | public function __construct($nbResults, $results)
32 | {
33 | $this->nbResults = $nbResults;
34 | $this->results = $results;
35 | }
36 |
37 | /**
38 | * {@inheritDoc}
39 | */
40 | public function getNbResults()
41 | {
42 | return $this->nbResults;
43 | }
44 |
45 | /**
46 | * {@inheritDoc}
47 | */
48 | public function getSlice($offset, $length)
49 | {
50 | return $this->results;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/MandangoAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Mandango\Query;
15 |
16 | /**
17 | * MandangoAdapter.
18 | *
19 | * @author Pablo Díez
20 | */
21 | class MandangoAdapter implements AdapterInterface
22 | {
23 | private $query;
24 |
25 | /**
26 | * Constructor.
27 | *
28 | * @param Query $query The query.
29 | */
30 | public function __construct(Query $query)
31 | {
32 | $this->query = $query;
33 | }
34 |
35 | /**
36 | * Returns the query.
37 | *
38 | * @return Query The query.
39 | */
40 | public function getQuery()
41 | {
42 | return $this->query;
43 | }
44 |
45 | /**
46 | * {@inheritdoc}
47 | */
48 | public function getNbResults()
49 | {
50 | return $this->query->count();
51 | }
52 |
53 | /**
54 | * {@inheritdoc}
55 | */
56 | public function getSlice($offset, $length)
57 | {
58 | return $this->query->limit($length)->skip($offset)->all();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/MongoAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | /**
15 | * MongoAdapter.
16 | *
17 | * @author Sergey Ponomaryov
18 | */
19 | class MongoAdapter implements AdapterInterface
20 | {
21 | private $cursor;
22 |
23 | /**
24 | * Constructor.
25 | *
26 | * @param \MongoCursor $cursor The cursor.
27 | */
28 | public function __construct(\MongoCursor $cursor)
29 | {
30 | $this->cursor = $cursor;
31 | }
32 |
33 | /**
34 | * Returns the cursor.
35 | *
36 | * @return \MongoCursor The cursor.
37 | */
38 | public function getCursor()
39 | {
40 | return $this->cursor;
41 | }
42 |
43 | /**
44 | * {@inheritdoc}
45 | */
46 | public function getNbResults()
47 | {
48 | return $this->cursor->count();
49 | }
50 |
51 | /**
52 | * {@inheritdoc}
53 | */
54 | public function getSlice($offset, $length)
55 | {
56 | $this->cursor->limit($length);
57 | $this->cursor->skip($offset);
58 |
59 | return $this->cursor;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/NullAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | /**
15 | * NullAdapter.
16 | *
17 | * @author Benjamin Dulau
18 | */
19 | class NullAdapter implements AdapterInterface
20 | {
21 | private $nbResults;
22 |
23 | /**
24 | * Constructor.
25 | *
26 | * @param integer $nbResults Total item count.
27 | */
28 | public function __construct($nbResults = 0)
29 | {
30 | $this->nbResults = (int) $nbResults;
31 | }
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function getNbResults()
37 | {
38 | return $this->nbResults;
39 | }
40 |
41 | /**
42 | * The following methods are derived from code of the Zend Framework
43 | * Code subject to the new BSD license (http://framework.zend.com/license/new-bsd).
44 | *
45 | * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
46 | *
47 | * {@inheritdoc}
48 | */
49 | public function getSlice($offset, $length)
50 | {
51 | if ($offset >= $this->nbResults) {
52 | return array();
53 | }
54 |
55 | $nullArrayLength = $this->calculateNullArrayLength($offset, $length);
56 |
57 | return $this->createNullArray($nullArrayLength);
58 | }
59 |
60 | private function calculateNullArrayLength($offset, $length)
61 | {
62 | $remainCount = $this->remainCount($offset);
63 | if ($length > $remainCount) {
64 | return $remainCount;
65 | }
66 |
67 | return $length;
68 | }
69 |
70 | private function remainCount($offset)
71 | {
72 | return $this->nbResults - $offset;
73 | }
74 |
75 | private function createNullArray($length)
76 | {
77 | return array_fill(0, $length, null);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/Propel2Adapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Propel\Runtime\ActiveQuery\ModelCriteria;
15 |
16 | /**
17 | * Propel2Adapter.
18 | *
19 | * @author Raphael YAN
20 | */
21 | class Propel2Adapter implements AdapterInterface
22 | {
23 | /**
24 | * @var ModelCriteria $query
25 | */
26 | private $query;
27 |
28 | /**
29 | * Constructor.
30 | *
31 | * @param ModelCriteria $query
32 | */
33 | public function __construct(ModelCriteria $query)
34 | {
35 | $this->query = $query;
36 | }
37 |
38 | /**
39 | * Returns the query.
40 | *
41 | * @return ModelCriteria
42 | */
43 | public function getQuery()
44 | {
45 | return $this->query;
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | public function getNbResults()
52 | {
53 | $q = clone $this->getQuery();
54 |
55 | $q->offset(0);
56 |
57 | return $q->count();
58 | }
59 |
60 | /**
61 | * {@inheritdoc}
62 | */
63 | public function getSlice($offset, $length)
64 | {
65 | $q = clone $this->getQuery();
66 |
67 | $q->limit($length);
68 | $q->offset($offset);
69 |
70 | return $q->find();
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/PropelAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | /**
15 | * PropelAdapter.
16 | *
17 | * @author William DURAND
18 | */
19 | class PropelAdapter implements AdapterInterface
20 | {
21 | private $query;
22 |
23 | /**
24 | * Constructor.
25 | *
26 | * @param \ModelCriteria $query
27 | */
28 | public function __construct($query)
29 | {
30 | $this->query = $query;
31 | }
32 |
33 | /**
34 | * Returns the query.
35 | *
36 | * @return \ModelCriteria
37 | */
38 | public function getQuery()
39 | {
40 | return $this->query;
41 | }
42 |
43 | /**
44 | * {@inheritdoc}
45 | */
46 | public function getNbResults()
47 | {
48 | $q = clone $this->getQuery();
49 |
50 | $q->limit(0);
51 | $q->offset(0);
52 |
53 | return $q->count();
54 | }
55 |
56 | /**
57 | * {@inheritdoc}
58 | */
59 | public function getSlice($offset, $length)
60 | {
61 | $q = clone $this->getQuery();
62 |
63 | $q->limit($length);
64 | $q->offset($offset);
65 |
66 | return $q->find();
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Adapter/SolariumAdapter.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Adapter;
13 |
14 | use Pagerfanta\Exception\InvalidArgumentException;
15 | use Solarium\QueryType\Select\Query\Query;
16 | use Solarium\Core\Client\Client;
17 | use Solarium_Query_Select;
18 | use Solarium_Client;
19 |
20 | /**
21 | * SolariumAdapter.
22 | *
23 | * @author Igor Wiedler
24 | */
25 | class SolariumAdapter implements AdapterInterface
26 | {
27 | private $client;
28 | private $query;
29 | private $resultSet;
30 | private $endPoint;
31 | private $resultSetStart;
32 | private $resultSetRows;
33 |
34 | /**
35 | * Constructor.
36 | *
37 | * @param Solarium_Client|Client $client A Solarium client.
38 | * @param Solarium_Query_Select|Query $query A Solarium select query.
39 | */
40 | public function __construct($client, $query)
41 | {
42 | $this->checkClient($client);
43 | $this->checkQuery($query);
44 |
45 | $this->client = $client;
46 | $this->query = $query;
47 | }
48 |
49 | private function checkClient($client)
50 | {
51 | if ($this->isClientInvalid($client)) {
52 | throw new InvalidArgumentException($this->getClientInvalidMessage($client));
53 | }
54 | }
55 |
56 | private function isClientInvalid($client)
57 | {
58 | return !($client instanceof Client) &&
59 | !($client instanceof Solarium_Client);
60 | }
61 |
62 | private function getClientInvalidMessage($client)
63 | {
64 | return sprintf('The client object should be a Solarium_Client or Solarium\Core\Client\Client instance, %s given',
65 | get_class($client)
66 | );
67 | }
68 |
69 | private function checkQuery($query)
70 | {
71 | if ($this->isQueryInvalid($query)) {
72 | throw new InvalidArgumentException($this->getQueryInvalidMessage($query));
73 | }
74 | }
75 |
76 | private function isQueryInvalid($query)
77 | {
78 | return !($query instanceof Query) &&
79 | !($query instanceof Solarium_Query_Select);
80 | }
81 |
82 | private function getQueryInvalidMessage($query)
83 | {
84 | return sprintf('The query object should be a Solarium_Query_Select or Solarium\QueryType\Select\Query\Query instance, %s given',
85 | get_class($query)
86 | );
87 | }
88 |
89 | /**
90 | * {@inheritdoc}
91 | */
92 | public function getNbResults()
93 | {
94 | return $this->getResultSet()->getNumFound();
95 | }
96 |
97 | /**
98 | * {@inheritdoc}
99 | */
100 | public function getSlice($offset, $length)
101 | {
102 | return $this->getResultSet($offset, $length);
103 | }
104 |
105 | /**
106 | * @param int $start
107 | * @param int $rows
108 | *
109 | * @return \Solarium_Result_Select|\Solarium\QueryType\Select\Result\Result
110 | **/
111 | public function getResultSet($start = null, $rows = null)
112 | {
113 | if ($this->resultSetStartAndRowsAreNotNullAndChange($start, $rows)) {
114 | $this->resultSetStart = $start;
115 | $this->resultSetRows = $rows;
116 |
117 | $this->modifyQuery();
118 | $this->clearResultSet();
119 | }
120 |
121 | if ($this->resultSetEmpty()) {
122 | $this->resultSet = $this->createResultSet();
123 | }
124 |
125 | return $this->resultSet;
126 | }
127 |
128 | private function resultSetStartAndRowsAreNotNullAndChange($start, $rows)
129 | {
130 | return $this->resultSetStartAndRowsAreNotNull($start, $rows) &&
131 | $this->resultSetStartAndRowsChange($start, $rows);
132 | }
133 |
134 | private function resultSetStartAndRowsAreNotNull($start, $rows)
135 | {
136 | return $start !== null && $rows !== null;
137 | }
138 |
139 | private function resultSetStartAndRowsChange($start, $rows)
140 | {
141 | return $start !== $this->resultSetStart || $rows !== $this->resultSetRows;
142 | }
143 |
144 | private function modifyQuery()
145 | {
146 | $this->query
147 | ->setStart($this->resultSetStart)
148 | ->setRows($this->resultSetRows);
149 | }
150 |
151 | private function createResultSet()
152 | {
153 | return $this->client->select($this->query, $this->endPoint);
154 | }
155 |
156 | private function clearResultSet()
157 | {
158 | $this->resultSet = null;
159 | }
160 |
161 | private function resultSetEmpty()
162 | {
163 | return $this->resultSet === null;
164 | }
165 |
166 | public function setEndPoint($endPoint)
167 | {
168 | $this->endPoint = $endPoint;
169 |
170 | return $this;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/Exception.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * Exception.
16 | *
17 | * @author Pablo Díez
18 | */
19 | interface Exception extends \Throwable
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/InvalidArgumentException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * InvalidArgumentException.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class InvalidArgumentException extends \InvalidArgumentException implements Exception
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/LessThan1CurrentPageException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * LessThan1CurrentPageException.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class LessThan1CurrentPageException extends NotValidCurrentPageException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/LessThan1MaxPerPageException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * LessThan1MaxPerPageException
16 | *
17 | * @author Pablo Díez
18 | */
19 | class LessThan1MaxPerPageException extends NotValidMaxPerPageException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/LogicException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * LogicException.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class LogicException extends \LogicException implements Exception
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/NotBooleanException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * @author Pablo Díez
16 | */
17 | class NotBooleanException extends InvalidArgumentException
18 | {
19 | }
20 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/NotIntegerCurrentPageException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * NotIntegerCurrentPageException.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class NotIntegerCurrentPageException extends NotValidCurrentPageException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/NotIntegerException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * NotIntegerItemException.
16 | *
17 | * @author Alexandre Fayeaux
18 | */
19 | class NotIntegerException extends InvalidArgumentException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/NotIntegerMaxPerPageException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * NotIntegerMaxPerPageException
16 | *
17 | * @author Pablo Díez
18 | */
19 | class NotIntegerMaxPerPageException extends NotValidMaxPerPageException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/NotValidCurrentPageException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * NotValidCurrentPageException.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class NotValidCurrentPageException extends InvalidArgumentException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/NotValidMaxPerPageException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * NotValidMaxPerPageException.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class NotValidMaxPerPageException extends InvalidArgumentException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Exception/OutOfRangeCurrentPageException.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\Exception;
13 |
14 | /**
15 | * OutOfRangeCurrentPageException.
16 | *
17 | * @author Pablo Díez
18 | */
19 | class OutOfRangeCurrentPageException extends NotValidCurrentPageException
20 | {
21 | }
22 |
--------------------------------------------------------------------------------
/src/Pagerfanta/Pagerfanta.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta;
13 |
14 | use OutOfBoundsException;
15 | use Pagerfanta\Adapter\AdapterInterface;
16 | use Pagerfanta\Exception\LogicException;
17 | use Pagerfanta\Exception\NotBooleanException;
18 | use Pagerfanta\Exception\NotIntegerException;
19 | use Pagerfanta\Exception\NotIntegerMaxPerPageException;
20 | use Pagerfanta\Exception\LessThan1MaxPerPageException;
21 | use Pagerfanta\Exception\NotIntegerCurrentPageException;
22 | use Pagerfanta\Exception\LessThan1CurrentPageException;
23 | use Pagerfanta\Exception\OutOfRangeCurrentPageException;
24 |
25 | /**
26 | * Represents a paginator.
27 | *
28 | * @author Pablo Díez
29 | */
30 | class Pagerfanta implements \Countable, \IteratorAggregate, \JsonSerializable, PagerfantaInterface
31 | {
32 | private $adapter;
33 | private $allowOutOfRangePages;
34 | private $normalizeOutOfRangePages;
35 | private $maxPerPage;
36 | private $currentPage;
37 | private $nbResults;
38 | private $currentPageResults;
39 |
40 | /**
41 | * @param AdapterInterface $adapter An adapter.
42 | */
43 | public function __construct(AdapterInterface $adapter)
44 | {
45 | $this->adapter = $adapter;
46 | $this->allowOutOfRangePages = false;
47 | $this->normalizeOutOfRangePages = false;
48 | $this->maxPerPage = 10;
49 | $this->currentPage = 1;
50 | }
51 |
52 | /**
53 | * Returns the adapter.
54 | *
55 | * @return AdapterInterface The adapter.
56 | */
57 | public function getAdapter()
58 | {
59 | return $this->adapter;
60 | }
61 |
62 | /**
63 | * Sets whether or not allow out of range pages.
64 | *
65 | * @param boolean $value
66 | *
67 | * @return self
68 | */
69 | public function setAllowOutOfRangePages($value)
70 | {
71 | $this->allowOutOfRangePages = $this->filterBoolean($value);
72 |
73 | return $this;
74 | }
75 |
76 | /**
77 | * Returns whether or not allow out of range pages.
78 | *
79 | * @return boolean
80 | */
81 | public function getAllowOutOfRangePages()
82 | {
83 | return $this->allowOutOfRangePages;
84 | }
85 |
86 | /**
87 | * Sets whether or not normalize out of range pages.
88 | *
89 | * @param boolean $value
90 | *
91 | * @return self
92 | */
93 | public function setNormalizeOutOfRangePages($value)
94 | {
95 | $this->normalizeOutOfRangePages = $this->filterBoolean($value);
96 |
97 | return $this;
98 | }
99 |
100 | /**
101 | * Returns whether or not normalize out of range pages.
102 | *
103 | * @return boolean
104 | */
105 | public function getNormalizeOutOfRangePages()
106 | {
107 | return $this->normalizeOutOfRangePages;
108 | }
109 |
110 | private function filterBoolean($value)
111 | {
112 | if (!is_bool($value)) {
113 | throw new NotBooleanException();
114 | }
115 |
116 | return $value;
117 | }
118 |
119 | /**
120 | * Sets the max per page.
121 | *
122 | * Tries to convert from string and float.
123 | *
124 | * @param integer $maxPerPage
125 | *
126 | * @return self
127 | *
128 | * @throws NotIntegerMaxPerPageException If the max per page is not an integer even converting.
129 | * @throws LessThan1MaxPerPageException If the max per page is less than 1.
130 | */
131 | public function setMaxPerPage($maxPerPage)
132 | {
133 | $this->maxPerPage = $this->filterMaxPerPage($maxPerPage);
134 | $this->resetForMaxPerPageChange();
135 |
136 | return $this;
137 | }
138 |
139 | private function filterMaxPerPage($maxPerPage)
140 | {
141 | $maxPerPage = $this->toInteger($maxPerPage);
142 | $this->checkMaxPerPage($maxPerPage);
143 |
144 | return $maxPerPage;
145 | }
146 |
147 | private function checkMaxPerPage($maxPerPage)
148 | {
149 | if (!is_int($maxPerPage)) {
150 | throw new NotIntegerMaxPerPageException();
151 | }
152 |
153 | if ($maxPerPage < 1) {
154 | throw new LessThan1MaxPerPageException();
155 | }
156 | }
157 |
158 | private function resetForMaxPerPageChange()
159 | {
160 | $this->currentPageResults = null;
161 | $this->nbResults = null;
162 | }
163 |
164 | /**
165 | * Returns the max per page.
166 | *
167 | * @return integer
168 | */
169 | public function getMaxPerPage()
170 | {
171 | return $this->maxPerPage;
172 | }
173 |
174 | /**
175 | * Sets the current page.
176 | *
177 | * Tries to convert from string and float.
178 | *
179 | * @param integer $currentPage
180 | *
181 | * @return self
182 | *
183 | * @throws NotIntegerCurrentPageException If the current page is not an integer even converting.
184 | * @throws LessThan1CurrentPageException If the current page is less than 1.
185 | * @throws OutOfRangeCurrentPageException If It is not allowed out of range pages and they are not normalized.
186 | */
187 | public function setCurrentPage($currentPage)
188 | {
189 | $this->useDeprecatedCurrentPageBooleanArguments(func_get_args());
190 |
191 | $this->currentPage = $this->filterCurrentPage($currentPage);
192 | $this->resetForCurrentPageChange();
193 |
194 | return $this;
195 | }
196 |
197 | private function useDeprecatedCurrentPageBooleanArguments($arguments)
198 | {
199 | $this->useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments);
200 | $this->useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments);
201 | }
202 |
203 | private function useDeprecatedCurrentPageAllowOutOfRangePagesBooleanArgument($arguments)
204 | {
205 | $index = 1;
206 | $method = 'setAllowOutOfRangePages';
207 |
208 | $this->useDeprecatedBooleanArgument($arguments, $index, $method);
209 | }
210 |
211 | private function useDeprecatedCurrentPageNormalizeOutOfRangePagesBooleanArgument($arguments)
212 | {
213 | $index = 2;
214 | $method = 'setNormalizeOutOfRangePages';
215 |
216 | $this->useDeprecatedBooleanArgument($arguments, $index, $method);
217 | }
218 |
219 | private function useDeprecatedBooleanArgument($arguments, $index, $method)
220 | {
221 | if (isset($arguments[$index])) {
222 | $this->$method($arguments[$index]);
223 | }
224 | }
225 |
226 | private function filterCurrentPage($currentPage)
227 | {
228 | $currentPage = $this->toInteger($currentPage);
229 | $this->checkCurrentPage($currentPage);
230 | $currentPage = $this->filterOutOfRangeCurrentPage($currentPage);
231 |
232 | return $currentPage;
233 | }
234 |
235 | private function checkCurrentPage($currentPage)
236 | {
237 | if (!is_int($currentPage)) {
238 | throw new NotIntegerCurrentPageException();
239 | }
240 |
241 | if ($currentPage < 1) {
242 | throw new LessThan1CurrentPageException();
243 | }
244 | }
245 |
246 | private function filterOutOfRangeCurrentPage($currentPage)
247 | {
248 | if ($this->notAllowedCurrentPageOutOfRange($currentPage)) {
249 | return $this->normalizeOutOfRangeCurrentPage($currentPage);
250 | }
251 |
252 | return $currentPage;
253 | }
254 |
255 | private function notAllowedCurrentPageOutOfRange($currentPage)
256 | {
257 | return !$this->getAllowOutOfRangePages() &&
258 | $this->currentPageOutOfRange($currentPage);
259 | }
260 |
261 | private function currentPageOutOfRange($currentPage)
262 | {
263 | return $currentPage > 1 && $currentPage > $this->getNbPages();
264 | }
265 |
266 | /**
267 | * @param int $currentPage
268 | *
269 | * @return int
270 | *
271 | * @throws OutOfRangeCurrentPageException If the page should not be normalized
272 | */
273 | private function normalizeOutOfRangeCurrentPage($currentPage)
274 | {
275 | if ($this->getNormalizeOutOfRangePages()) {
276 | return $this->getNbPages();
277 | }
278 |
279 | throw new OutOfRangeCurrentPageException(sprintf('Page "%d" does not exist. The currentPage must be inferior to "%d"', $currentPage, $this->getNbPages()));
280 | }
281 |
282 | private function resetForCurrentPageChange()
283 | {
284 | $this->currentPageResults = null;
285 | }
286 |
287 | /**
288 | * Returns the current page.
289 | *
290 | * @return integer
291 | */
292 | public function getCurrentPage()
293 | {
294 | return $this->currentPage;
295 | }
296 |
297 | /**
298 | * Returns the results for the current page.
299 | *
300 | * @return array|\Traversable
301 | */
302 | public function getCurrentPageResults()
303 | {
304 | if ($this->notCachedCurrentPageResults()) {
305 | $this->currentPageResults = $this->getCurrentPageResultsFromAdapter();
306 | }
307 |
308 | return $this->currentPageResults;
309 | }
310 |
311 | private function notCachedCurrentPageResults()
312 | {
313 | return $this->currentPageResults === null;
314 | }
315 |
316 | private function getCurrentPageResultsFromAdapter()
317 | {
318 | $offset = $this->calculateOffsetForCurrentPageResults();
319 | $length = $this->getMaxPerPage();
320 |
321 | return $this->adapter->getSlice($offset, $length);
322 | }
323 |
324 | private function calculateOffsetForCurrentPageResults()
325 | {
326 | return ($this->getCurrentPage() - 1) * $this->getMaxPerPage();
327 | }
328 |
329 | /**
330 | * Calculates the current page offset start
331 | *
332 | * @return int
333 | */
334 | public function getCurrentPageOffsetStart()
335 | {
336 | return $this->getNbResults() ?
337 | $this->calculateOffsetForCurrentPageResults() + 1 :
338 | 0;
339 | }
340 |
341 | /**
342 | * Calculates the current page offset end
343 | *
344 | * @return int
345 | */
346 | public function getCurrentPageOffsetEnd()
347 | {
348 | return $this->hasNextPage() ?
349 | $this->getCurrentPage() * $this->getMaxPerPage() :
350 | $this->getNbResults();
351 | }
352 |
353 | /**
354 | * Returns the number of results.
355 | *
356 | * @return integer
357 | */
358 | public function getNbResults()
359 | {
360 | if ($this->notCachedNbResults()) {
361 | $this->nbResults = $this->getAdapter()->getNbResults();
362 | }
363 |
364 | return $this->nbResults;
365 | }
366 |
367 | private function notCachedNbResults()
368 | {
369 | return $this->nbResults === null;
370 | }
371 |
372 | /**
373 | * Returns the number of pages.
374 | *
375 | * @return integer
376 | */
377 | public function getNbPages()
378 | {
379 | $nbPages = $this->calculateNbPages();
380 |
381 | if ($nbPages == 0) {
382 | return $this->minimumNbPages();
383 | }
384 |
385 | return $nbPages;
386 | }
387 |
388 | private function calculateNbPages()
389 | {
390 | return (int) ceil($this->getNbResults() / $this->getMaxPerPage());
391 | }
392 |
393 | private function minimumNbPages()
394 | {
395 | return 1;
396 | }
397 |
398 | /**
399 | * Returns if the number of results is higher than the max per page.
400 | *
401 | * @return boolean
402 | */
403 | public function haveToPaginate()
404 | {
405 | return $this->getNbResults() > $this->maxPerPage;
406 | }
407 |
408 | /**
409 | * Returns whether there is previous page or not.
410 | *
411 | * @return boolean
412 | */
413 | public function hasPreviousPage()
414 | {
415 | return $this->currentPage > 1;
416 | }
417 |
418 | /**
419 | * Returns the previous page.
420 | *
421 | * @return integer
422 | *
423 | * @throws LogicException If there is no previous page.
424 | */
425 | public function getPreviousPage()
426 | {
427 | if (!$this->hasPreviousPage()) {
428 | throw new LogicException('There is no previous page.');
429 | }
430 |
431 | return $this->currentPage - 1;
432 | }
433 |
434 | /**
435 | * Returns whether there is next page or not.
436 | *
437 | * @return boolean
438 | */
439 | public function hasNextPage()
440 | {
441 | return $this->currentPage < $this->getNbPages();
442 | }
443 |
444 | /**
445 | * Returns the next page.
446 | *
447 | * @return integer
448 | *
449 | * @throws LogicException If there is no next page.
450 | */
451 | public function getNextPage()
452 | {
453 | if (!$this->hasNextPage()) {
454 | throw new LogicException('There is no next page.');
455 | }
456 |
457 | return $this->currentPage + 1;
458 | }
459 |
460 | /**
461 | * Implements the \Countable interface.
462 | *
463 | * @return integer The number of results.
464 | */
465 | public function count()
466 | {
467 | return $this->getNbResults();
468 | }
469 |
470 | /**
471 | * Implements the \IteratorAggregate interface.
472 | *
473 | * @return \ArrayIterator instance with the current results.
474 | */
475 | public function getIterator()
476 | {
477 | $results = $this->getCurrentPageResults();
478 |
479 | if ($results instanceof \Iterator) {
480 | return $results;
481 | }
482 |
483 | if ($results instanceof \IteratorAggregate) {
484 | return $results->getIterator();
485 | }
486 |
487 | return new \ArrayIterator($results);
488 | }
489 |
490 | /**
491 | * Implements the \JsonSerializable interface.
492 | *
493 | * @return array current page results
494 | */
495 | public function jsonSerialize()
496 | {
497 | $results = $this->getCurrentPageResults();
498 | if ($results instanceof \Traversable) {
499 | return iterator_to_array($results);
500 | }
501 |
502 | return $results;
503 | }
504 |
505 | private function toInteger($value)
506 | {
507 | if ($this->needsToIntegerConversion($value)) {
508 | return (int) $value;
509 | }
510 |
511 | return $value;
512 | }
513 |
514 | private function needsToIntegerConversion($value)
515 | {
516 | return (is_string($value) || is_float($value)) && (int) $value == $value;
517 | }
518 |
519 | /**
520 | * Get page number of the item at specified position (1-based index)
521 | *
522 | * @param integer $position
523 | *
524 | * @return integer
525 | */
526 | public function getPageNumberForItemAtPosition($position)
527 | {
528 | if (!is_int($position)) {
529 | throw new NotIntegerException();
530 | }
531 |
532 | if ($this->getNbResults() < $position) {
533 | throw new OutOfBoundsException(sprintf(
534 | 'Item requested at position %d, but there are only %d items.',
535 | $position,
536 | $this->getNbResults()
537 | ));
538 | }
539 |
540 | return (int) ceil($position/$this->getMaxPerPage());
541 | }
542 | }
543 |
--------------------------------------------------------------------------------
/src/Pagerfanta/PagerfantaInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta;
13 |
14 | /**
15 | * @deprecated
16 | */
17 | interface PagerfantaInterface
18 | {
19 | }
20 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/DefaultView.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\PagerfantaInterface;
15 | use Pagerfanta\View\Template\TemplateInterface;
16 | use Pagerfanta\View\Template\DefaultTemplate;
17 |
18 | /**
19 | * @author Pablo Díez
20 | */
21 | class DefaultView implements ViewInterface
22 | {
23 | private $template;
24 |
25 | private $pagerfanta;
26 | private $proximity;
27 |
28 | private $currentPage;
29 | private $nbPages;
30 |
31 | private $startPage;
32 | private $endPage;
33 |
34 | public function __construct(TemplateInterface $template = null)
35 | {
36 | $this->template = $template ?: $this->createDefaultTemplate();
37 | }
38 |
39 | protected function createDefaultTemplate()
40 | {
41 | return new DefaultTemplate();
42 | }
43 |
44 | /**
45 | * {@inheritdoc}
46 | */
47 | public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array())
48 | {
49 | $this->initializePagerfanta($pagerfanta);
50 | $this->initializeOptions($options);
51 |
52 | $this->configureTemplate($routeGenerator, $options);
53 |
54 | return $this->generate();
55 | }
56 |
57 | private function initializePagerfanta(PagerfantaInterface $pagerfanta)
58 | {
59 | $this->pagerfanta = $pagerfanta;
60 |
61 | $this->currentPage = $pagerfanta->getCurrentPage();
62 | $this->nbPages = $pagerfanta->getNbPages();
63 | }
64 |
65 | private function initializeOptions($options)
66 | {
67 | $this->proximity = isset($options['proximity']) ?
68 | (int) $options['proximity'] :
69 | $this->getDefaultProximity();
70 | }
71 |
72 | protected function getDefaultProximity()
73 | {
74 | return 2;
75 | }
76 |
77 | private function configureTemplate($routeGenerator, $options)
78 | {
79 | $this->template->setRouteGenerator($routeGenerator);
80 | $this->template->setOptions($options);
81 | }
82 |
83 | private function generate()
84 | {
85 | $pages = $this->generatePages();
86 |
87 | return $this->generateContainer($pages);
88 | }
89 |
90 | private function generateContainer($pages)
91 | {
92 | return str_replace('%pages%', $pages, $this->template->container());
93 | }
94 |
95 | private function generatePages()
96 | {
97 | $this->calculateStartAndEndPage();
98 |
99 | return $this->previous().
100 | $this->first().
101 | $this->secondIfStartIs3().
102 | $this->dotsIfStartIsOver3().
103 | $this->pages().
104 | $this->dotsIfEndIsUnder3ToLast().
105 | $this->secondToLastIfEndIs3ToLast().
106 | $this->last().
107 | $this->next();
108 | }
109 |
110 | private function calculateStartAndEndPage()
111 | {
112 | $startPage = $this->currentPage - $this->proximity;
113 | $endPage = $this->currentPage + $this->proximity;
114 |
115 | if ($this->startPageUnderflow($startPage)) {
116 | $endPage = $this->calculateEndPageForStartPageUnderflow($startPage, $endPage);
117 | $startPage = 1;
118 | }
119 | if ($this->endPageOverflow($endPage)) {
120 | $startPage = $this->calculateStartPageForEndPageOverflow($startPage, $endPage);
121 | $endPage = $this->nbPages;
122 | }
123 |
124 | $this->startPage = $startPage;
125 | $this->endPage = $endPage;
126 | }
127 |
128 | private function startPageUnderflow($startPage)
129 | {
130 | return $startPage < 1;
131 | }
132 |
133 | private function endPageOverflow($endPage)
134 | {
135 | return $endPage > $this->nbPages;
136 | }
137 |
138 | private function calculateEndPageForStartPageUnderflow($startPage, $endPage)
139 | {
140 | return min($endPage + (1 - $startPage), $this->nbPages);
141 | }
142 |
143 | private function calculateStartPageForEndPageOverflow($startPage, $endPage)
144 | {
145 | return max($startPage - ($endPage - $this->nbPages), 1);
146 | }
147 |
148 | private function previous()
149 | {
150 | if ($this->pagerfanta->hasPreviousPage()) {
151 | return $this->template->previousEnabled($this->pagerfanta->getPreviousPage());
152 | }
153 |
154 | return $this->template->previousDisabled();
155 | }
156 |
157 | private function first()
158 | {
159 | if ($this->startPage > 1) {
160 | return $this->template->first();
161 | }
162 | }
163 |
164 | private function secondIfStartIs3()
165 | {
166 | if ($this->startPage == 3) {
167 | return $this->template->page(2);
168 | }
169 | }
170 |
171 | private function dotsIfStartIsOver3()
172 | {
173 | if ($this->startPage > 3) {
174 | return $this->template->separator();
175 | }
176 | }
177 |
178 | private function pages()
179 | {
180 | $pages = '';
181 |
182 | foreach (range($this->startPage, $this->endPage) as $page) {
183 | $pages .= $this->page($page);
184 | }
185 |
186 | return $pages;
187 | }
188 |
189 | private function page($page)
190 | {
191 | if ($page == $this->currentPage) {
192 | return $this->template->current($page);
193 | }
194 |
195 | return $this->template->page($page);
196 | }
197 |
198 | private function dotsIfEndIsUnder3ToLast()
199 | {
200 | if ($this->endPage < $this->toLast(3)) {
201 | return $this->template->separator();
202 | }
203 | }
204 |
205 | private function secondToLastIfEndIs3ToLast()
206 | {
207 | if ($this->endPage == $this->toLast(3)) {
208 | return $this->template->page($this->toLast(2));
209 | }
210 | }
211 |
212 | private function toLast($n)
213 | {
214 | return $this->pagerfanta->getNbPages() - ($n - 1);
215 | }
216 |
217 | private function last()
218 | {
219 | if ($this->pagerfanta->getNbPages() > $this->endPage) {
220 | return $this->template->last($this->pagerfanta->getNbPages());
221 | }
222 | }
223 |
224 | private function next()
225 | {
226 | if ($this->pagerfanta->hasNextPage()) {
227 | return $this->template->nextEnabled($this->pagerfanta->getNextPage());
228 | }
229 |
230 | return $this->template->nextDisabled();
231 | }
232 |
233 | /**
234 | * {@inheritdoc}
235 | */
236 | public function getName()
237 | {
238 | return 'default';
239 | }
240 | }
241 |
242 | /*
243 |
244 | CSS:
245 |
246 | .pagerfanta {
247 | }
248 |
249 | .pagerfanta a,
250 | .pagerfanta span {
251 | display: inline-block;
252 | border: 1px solid blue;
253 | color: blue;
254 | margin-right: .2em;
255 | padding: .25em .35em;
256 | }
257 |
258 | .pagerfanta a {
259 | text-decoration: none;
260 | }
261 |
262 | .pagerfanta a:hover {
263 | background: #ccf;
264 | }
265 |
266 | .pagerfanta .dots {
267 | border-width: 0;
268 | }
269 |
270 | .pagerfanta .current {
271 | background: #ccf;
272 | font-weight: bold;
273 | }
274 |
275 | .pagerfanta .disabled {
276 | border-color: #ccf;
277 | color: #ccf;
278 | }
279 |
280 | COLORS:
281 |
282 | .pagerfanta a,
283 | .pagerfanta span {
284 | border-color: blue;
285 | color: blue;
286 | }
287 |
288 | .pagerfanta a:hover {
289 | background: #ccf;
290 | }
291 |
292 | .pagerfanta .current {
293 | background: #ccf;
294 | }
295 |
296 | .pagerfanta .disabled {
297 | border-color: #ccf;
298 | color: #cf;
299 | }
300 |
301 | */
302 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/OptionableView.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\PagerfantaInterface;
15 |
16 | /**
17 | * OptionableView.
18 | *
19 | * This view renders another view with a default options to reuse them in a project.
20 | *
21 | * @author Pablo Díez
22 | */
23 | class OptionableView implements ViewInterface
24 | {
25 | private $view;
26 | private $defaultOptions;
27 |
28 | /**
29 | * Constructor.
30 | *
31 | * @param ViewInterface $view A view.
32 | * @param array $defaultOptions An array of default options.
33 | */
34 | public function __construct(ViewInterface $view, array $defaultOptions)
35 | {
36 | $this->view = $view;
37 | $this->defaultOptions = $defaultOptions;
38 | }
39 |
40 | /**
41 | * {@inheritdoc}
42 | */
43 | public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array())
44 | {
45 | return $this->view->render($pagerfanta, $routeGenerator, array_merge($this->defaultOptions, $options));
46 | }
47 |
48 | /**
49 | * {@inheritdoc}
50 | */
51 | public function getName()
52 | {
53 | return 'optionable';
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/SemanticUiView.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\View\Template\SemanticUiTemplate;
15 |
16 | /**
17 | * SemanticUiView.
18 | *
19 | * View that can be used with the pagination module
20 | * from the Semantic UI CSS Toolkit
21 | * http://semantic-ui.com/
22 | *
23 | * @author Loïc Frémont
24 | */
25 | class SemanticUiView extends DefaultView
26 | {
27 | protected function createDefaultTemplate()
28 | {
29 | return new SemanticUiTemplate();
30 | }
31 |
32 | protected function getDefaultProximity()
33 | {
34 | return 3;
35 | }
36 |
37 | /**
38 | * {@inheritdoc}
39 | */
40 | public function getName()
41 | {
42 | return 'semantic_ui';
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/Template/DefaultTemplate.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View\Template;
13 |
14 | /**
15 | * @author Pablo Díez
16 | */
17 | class DefaultTemplate extends Template
18 | {
19 | static protected $defaultOptions = array(
20 | 'prev_message' => 'Previous',
21 | 'next_message' => 'Next',
22 | 'css_disabled_class' => 'disabled',
23 | 'css_dots_class' => 'dots',
24 | 'css_current_class' => 'current',
25 | 'dots_text' => '...',
26 | 'container_template' => '',
27 | 'page_template' => '%text%',
28 | 'span_template' => '%text%',
29 | 'rel_previous' => 'prev',
30 | 'rel_next' => 'next'
31 | );
32 |
33 | public function container()
34 | {
35 | return $this->option('container_template');
36 | }
37 |
38 | public function page($page)
39 | {
40 | $text = $page;
41 |
42 | return $this->pageWithText($page, $text);
43 | }
44 |
45 | public function pageWithText($page, $text, $rel = null)
46 | {
47 | $search = array('%href%', '%text%', '%rel%');
48 |
49 | $href = $this->generateRoute($page);
50 | $replace = $rel ? array($href, $text, ' rel="' . $rel . '"') : array($href, $text, '');
51 |
52 | return str_replace($search, $replace, $this->option('page_template'));
53 | }
54 |
55 | public function previousDisabled()
56 | {
57 | return $this->generateSpan($this->option('css_disabled_class'), $this->option('prev_message'));
58 | }
59 |
60 | public function previousEnabled($page)
61 | {
62 | return $this->pageWithText($page, $this->option('prev_message'), $this->option('rel_previous'));
63 | }
64 |
65 | public function nextDisabled()
66 | {
67 | return $this->generateSpan($this->option('css_disabled_class'), $this->option('next_message'));
68 | }
69 |
70 | public function nextEnabled($page)
71 | {
72 | return $this->pageWithText($page, $this->option('next_message'), $this->option('rel_next'));
73 | }
74 |
75 | public function first()
76 | {
77 | return $this->page(1);
78 | }
79 |
80 | public function last($page)
81 | {
82 | return $this->page($page);
83 | }
84 |
85 | public function current($page)
86 | {
87 | return $this->generateSpan($this->option('css_current_class'), $page);
88 | }
89 |
90 | public function separator()
91 | {
92 | return $this->generateSpan($this->option('css_dots_class'), $this->option('dots_text'));
93 | }
94 |
95 | private function generateSpan($class, $page)
96 | {
97 | $search = array('%class%', '%text%');
98 | $replace = array($class, $page);
99 |
100 | return str_replace($search, $replace, $this->option('span_template'));
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/Template/SemanticUiTemplate.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View\Template;
13 |
14 | /**
15 | * @author Loïc Frémont
16 | */
17 | class SemanticUiTemplate extends Template
18 | {
19 | static protected $defaultOptions = array(
20 | 'prev_message' => '← Previous',
21 | 'next_message' => 'Next →',
22 | 'dots_message' => '…',
23 | 'active_suffix' => '',
24 | 'css_container_class' => 'ui stackable fluid pagination menu',
25 | 'css_item_class' => 'item',
26 | 'css_prev_class' => 'prev',
27 | 'css_next_class' => 'next',
28 | 'css_disabled_class' => 'disabled',
29 | 'css_dots_class' => 'disabled',
30 | 'css_active_class' => 'active',
31 | );
32 |
33 | public function container()
34 | {
35 | return sprintf('%%pages%%
',
36 | $this->option('css_container_class')
37 | );
38 | }
39 |
40 | public function page($page)
41 | {
42 | $text = $page;
43 |
44 | return $this->pageWithText($page, $text);
45 | }
46 |
47 | public function pageWithText($page, $text)
48 | {
49 | $class = null;
50 |
51 | return $this->pageWithTextAndClass($page, $text, $class);
52 | }
53 |
54 | private function pageWithTextAndClass($page, $text, $class)
55 | {
56 | $href = $this->generateRoute($page);
57 |
58 | return $this->link($class, $href, $text);
59 | }
60 |
61 | public function previousDisabled()
62 | {
63 | $class = $this->previousDisabledClass();
64 | $text = $this->option('prev_message');
65 |
66 | return $this->div($class, $text);
67 | }
68 |
69 | private function previousDisabledClass()
70 | {
71 | return $this->option('css_prev_class').' '.$this->option('css_disabled_class');
72 | }
73 |
74 | public function previousEnabled($page)
75 | {
76 | $text = $this->option('prev_message');
77 | $class = $this->option('css_prev_class');
78 |
79 | return $this->pageWithTextAndClass($page, $text, $class);
80 | }
81 |
82 | public function nextDisabled()
83 | {
84 | $class = $this->nextDisabledClass();
85 | $text = $this->option('next_message');
86 |
87 | return $this->div($class, $text);
88 | }
89 |
90 | private function nextDisabledClass()
91 | {
92 | return $this->option('css_next_class').' '.$this->option('css_disabled_class');
93 | }
94 |
95 | public function nextEnabled($page)
96 | {
97 | $text = $this->option('next_message');
98 | $class = $this->option('css_next_class');
99 |
100 | return $this->pageWithTextAndClass($page, $text, $class);
101 | }
102 |
103 | public function first()
104 | {
105 | return $this->page(1);
106 | }
107 |
108 | public function last($page)
109 | {
110 | return $this->page($page);
111 | }
112 |
113 | public function current($page)
114 | {
115 | $text = trim($page.' '.$this->option('active_suffix'));
116 | $class = $this->option('css_active_class');
117 |
118 | return $this->div($class, $text);
119 | }
120 |
121 | public function separator()
122 | {
123 | $class = $this->option('css_dots_class');
124 | $text = $this->option('dots_message');
125 |
126 | return $this->div($class, $text);
127 | }
128 |
129 | private function link($class, $href, $text)
130 | {
131 | $item_class = $this->option('css_item_class');
132 |
133 | return sprintf('%s', $item_class, $class, $href, $text);
134 | }
135 |
136 | private function div($class, $text)
137 | {
138 | $item_class = $this->option('css_item_class');
139 |
140 | return sprintf('%s
', $item_class, $class, $text);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/Template/Template.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View\Template;
13 |
14 | /**
15 | * @author Pablo Díez
16 | */
17 | abstract class Template implements TemplateInterface
18 | {
19 | protected static $defaultOptions = array();
20 |
21 | private $routeGenerator;
22 | private $options;
23 |
24 | public function __construct()
25 | {
26 | $this->initializeOptions();
27 | }
28 |
29 | public function setRouteGenerator($routeGenerator)
30 | {
31 | $this->routeGenerator = $routeGenerator;
32 | }
33 |
34 | public function setOptions(array $options)
35 | {
36 | $this->options = array_merge($this->options, $options);
37 | }
38 |
39 | private function initializeOptions()
40 | {
41 | $this->options = static::$defaultOptions;
42 | }
43 |
44 | protected function generateRoute($page)
45 | {
46 | return call_user_func($this->getRouteGenerator(), $page);
47 | }
48 |
49 | private function getRouteGenerator()
50 | {
51 | if (!$this->routeGenerator) {
52 | throw new \RuntimeException('There is no route generator.');
53 | }
54 |
55 | return $this->routeGenerator;
56 | }
57 |
58 | protected function option($name)
59 | {
60 | if (!isset($this->options[$name])) {
61 | throw new \InvalidArgumentException(sprintf('The option "%s" does not exist.', $name));
62 | }
63 |
64 | return $this->options[$name];
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/Template/TemplateInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View\Template;
13 |
14 | /**
15 | * @author Pablo Díez
16 | */
17 | interface TemplateInterface
18 | {
19 | /**
20 | * Renders the container for the pagination.
21 | *
22 | * The %pages% placeholder will be replaced by the rendering of pages
23 | *
24 | * @return string
25 | */
26 | public function container();
27 |
28 | /**
29 | * Renders a given page.
30 | *
31 | * @param int $page
32 | *
33 | * @return string
34 | */
35 | public function page($page);
36 |
37 | /**
38 | * Renders a given page with a specified text.
39 | *
40 | * @param int $page
41 | * @param string $text
42 | *
43 | * @return string
44 | */
45 | public function pageWithText($page, $text);
46 |
47 | /**
48 | * Renders the disabled state of the previous page.
49 | *
50 | * @return string
51 | */
52 | public function previousDisabled();
53 |
54 | /**
55 | * Renders the enabled state of the previous page.
56 | *
57 | * @param int $page
58 | *
59 | * @return string
60 | */
61 | public function previousEnabled($page);
62 |
63 | /**
64 | * Renders the disabled state of the next page.
65 | *
66 | * @return string
67 | */
68 | public function nextDisabled();
69 |
70 | /**
71 | * Renders the enabled state of the next page.
72 | *
73 | * @param int $page
74 | *
75 | * @return string
76 | */
77 | public function nextEnabled($page);
78 |
79 | /**
80 | * Renders the first page.
81 | *
82 | * @return string
83 | */
84 | public function first();
85 |
86 | /**
87 | * Renders the last page.
88 | *
89 | * @param int $page
90 | *
91 | * @return string
92 | */
93 | public function last($page);
94 |
95 | /**
96 | * Renders the current page.
97 | *
98 | * @param int $page
99 | *
100 | * @return string
101 | */
102 | public function current($page);
103 |
104 | /**
105 | * Renders the separator between pages.
106 | *
107 | * @return string
108 | */
109 | public function separator();
110 | }
111 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/Template/TwitterBootstrap3Template.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View\Template;
13 |
14 | /**
15 | * TwitterBootstrap3Template
16 | */
17 | class TwitterBootstrap3Template extends TwitterBootstrapTemplate
18 | {
19 | public function __construct()
20 | {
21 | parent::__construct();
22 |
23 | $this->setOptions(array('active_suffix' => '(current)'));
24 | }
25 |
26 | public function container()
27 | {
28 | return sprintf('',
29 | $this->option('css_container_class')
30 | );
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/Template/TwitterBootstrap4Template.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View\Template;
13 |
14 | /**
15 | * TwitterBootstrap4Template
16 | */
17 | class TwitterBootstrap4Template extends TwitterBootstrap3Template
18 | {
19 | protected function linkLi($class, $href, $text, $rel = null)
20 | {
21 | $liClass = implode(' ', array_filter(array('page-item', $class)));
22 | $rel = $rel ? sprintf(' rel="%s"', $rel) : '';
23 |
24 | return sprintf('%s', $liClass, $href, $rel, $text);
25 | }
26 |
27 | protected function spanLi($class, $text)
28 | {
29 | $liClass = implode(' ', array_filter(array('page-item', $class)));
30 |
31 | return sprintf('%s', $liClass, $text);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/Template/TwitterBootstrapTemplate.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View\Template;
13 |
14 | /**
15 | * @author Pablo Díez
16 | */
17 | class TwitterBootstrapTemplate extends Template
18 | {
19 | static protected $defaultOptions = array(
20 | 'prev_message' => '← Previous',
21 | 'next_message' => 'Next →',
22 | 'dots_message' => '…',
23 | 'active_suffix' => '',
24 | 'css_container_class' => 'pagination',
25 | 'css_prev_class' => 'prev',
26 | 'css_next_class' => 'next',
27 | 'css_disabled_class' => 'disabled',
28 | 'css_dots_class' => 'disabled',
29 | 'css_active_class' => 'active',
30 | 'rel_previous' => 'prev',
31 | 'rel_next' => 'next'
32 | );
33 |
34 | public function container()
35 | {
36 | return sprintf('',
37 | $this->option('css_container_class')
38 | );
39 | }
40 |
41 | public function page($page)
42 | {
43 | $text = $page;
44 |
45 | return $this->pageWithText($page, $text);
46 | }
47 |
48 | public function pageWithText($page, $text)
49 | {
50 | $class = null;
51 |
52 | return $this->pageWithTextAndClass($page, $text, $class);
53 | }
54 |
55 | private function pageWithTextAndClass($page, $text, $class, $rel = null)
56 | {
57 | $href = $this->generateRoute($page);
58 |
59 | return $this->linkLi($class, $href, $text, $rel);
60 | }
61 |
62 | public function previousDisabled()
63 | {
64 | $class = $this->previousDisabledClass();
65 | $text = $this->option('prev_message');
66 |
67 | return $this->spanLi($class, $text);
68 | }
69 |
70 | private function previousDisabledClass()
71 | {
72 | return $this->option('css_prev_class').' '.$this->option('css_disabled_class');
73 | }
74 |
75 | public function previousEnabled($page)
76 | {
77 | $text = $this->option('prev_message');
78 | $class = $this->option('css_prev_class');
79 | $rel = $this->option('rel_previous');
80 |
81 | return $this->pageWithTextAndClass($page, $text, $class, $rel);
82 | }
83 |
84 | public function nextDisabled()
85 | {
86 | $class = $this->nextDisabledClass();
87 | $text = $this->option('next_message');
88 |
89 | return $this->spanLi($class, $text);
90 | }
91 |
92 | private function nextDisabledClass()
93 | {
94 | return $this->option('css_next_class').' '.$this->option('css_disabled_class');
95 | }
96 |
97 | public function nextEnabled($page)
98 | {
99 | $text = $this->option('next_message');
100 | $class = $this->option('css_next_class');
101 | $rel = $this->option('rel_next');
102 |
103 | return $this->pageWithTextAndClass($page, $text, $class, $rel);
104 | }
105 |
106 | public function first()
107 | {
108 | return $this->page(1);
109 | }
110 |
111 | public function last($page)
112 | {
113 | return $this->page($page);
114 | }
115 |
116 | public function current($page)
117 | {
118 | $text = trim($page.' '.$this->option('active_suffix'));
119 | $class = $this->option('css_active_class');
120 |
121 | return $this->spanLi($class, $text);
122 | }
123 |
124 | public function separator()
125 | {
126 | $class = $this->option('css_dots_class');
127 | $text = $this->option('dots_message');
128 |
129 | return $this->spanLi($class, $text);
130 | }
131 |
132 | protected function linkLi($class, $href, $text, $rel = null)
133 | {
134 | $liClass = $class ? sprintf(' class="%s"', $class) : '';
135 | $rel = $rel ? sprintf(' rel="%s"', $rel) : '';
136 |
137 | return sprintf('%s', $liClass, $href, $rel, $text);
138 | }
139 |
140 | protected function spanLi($class, $text)
141 | {
142 | $liClass = $class ? sprintf(' class="%s"', $class) : '';
143 |
144 | return sprintf('%s', $liClass, $text);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/TwitterBootstrap3View.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\View\Template\TwitterBootstrap3Template;
15 |
16 | /**
17 | * TwitterBootstrap3View.
18 | *
19 | * View that can be used with the pagination module
20 | * from the Twitter Bootstrap3 CSS Toolkit
21 | * http://getbootstrap.com/
22 | *
23 | */
24 | class TwitterBootstrap3View extends TwitterBootstrapView
25 | {
26 | protected function createDefaultTemplate()
27 | {
28 | return new TwitterBootstrap3Template();
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function getName()
35 | {
36 | return 'twitter_bootstrap3';
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/TwitterBootstrap4View.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\View\Template\TwitterBootstrap4Template;
15 |
16 | /**
17 | * TwitterBootstrap4View.
18 | *
19 | * View that can be used with the pagination module
20 | * from the Twitter Bootstrap4 CSS Toolkit
21 | * http://getbootstrap.com/
22 | *
23 | */
24 | class TwitterBootstrap4View extends TwitterBootstrapView
25 | {
26 | protected function createDefaultTemplate()
27 | {
28 | return new TwitterBootstrap4Template();
29 | }
30 |
31 | /**
32 | * {@inheritdoc}
33 | */
34 | public function getName()
35 | {
36 | return 'twitter_bootstrap4';
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/TwitterBootstrapView.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\View\Template\TwitterBootstrapTemplate;
15 |
16 | /**
17 | * TwitterBootstrapView.
18 | *
19 | * View that can be used with the pagination module
20 | * from the Twitter Bootstrap CSS Toolkit
21 | * http://twitter.github.com/bootstrap/
22 | *
23 | * @author Pablo Díez
24 | * @author Jan Sorgalla
25 | */
26 | class TwitterBootstrapView extends DefaultView
27 | {
28 | protected function createDefaultTemplate()
29 | {
30 | return new TwitterBootstrapTemplate();
31 | }
32 |
33 | protected function getDefaultProximity()
34 | {
35 | return 3;
36 | }
37 |
38 | /**
39 | * {@inheritdoc}
40 | */
41 | public function getName()
42 | {
43 | return 'twitter_bootstrap';
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/ViewFactory.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\Exception\InvalidArgumentException;
15 |
16 | /**
17 | * ViewFactory.
18 | *
19 | * @author Pablo Díez
20 | */
21 | class ViewFactory implements ViewFactoryInterface
22 | {
23 | private $views;
24 |
25 | /**
26 | * Constructor.
27 | */
28 | public function __construct()
29 | {
30 | $this->views = array();
31 | }
32 |
33 | /**
34 | * {@inheritdoc}
35 | */
36 | public function set($name, ViewInterface $view)
37 | {
38 | $this->views[$name] = $view;
39 | }
40 |
41 | /**
42 | * {@inheritdoc}
43 | */
44 | public function has($name)
45 | {
46 | return isset($this->views[$name]);
47 | }
48 |
49 | /**
50 | * {@inheritdoc}
51 | */
52 | public function add(array $views)
53 | {
54 | foreach ($views as $name => $view) {
55 | $this->set($name, $view);
56 | }
57 | }
58 |
59 | /**
60 | * {@inheritdoc}
61 | */
62 | public function get($name)
63 | {
64 | if (!$this->has($name)) {
65 | throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name));
66 | }
67 |
68 | return $this->views[$name];
69 | }
70 |
71 | /**
72 | * {@inheritdoc}
73 | */
74 | public function remove($name)
75 | {
76 | if (!$this->has($name)) {
77 | throw new InvalidArgumentException(sprintf('The view "%s" does not exist.', $name));
78 | }
79 |
80 | unset($this->views[$name]);
81 | }
82 |
83 | /**
84 | * {@inheritdoc}
85 | */
86 | public function all()
87 | {
88 | return $this->views;
89 | }
90 |
91 | /**
92 | * {@inheritdoc}
93 | */
94 | public function clear()
95 | {
96 | $this->views = array();
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/ViewFactoryInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | /**
15 | * ViewFactoryInterface.
16 | *
17 | * @author Pablo Díez
18 | */
19 | interface ViewFactoryInterface
20 | {
21 | /**
22 | * Sets a view.
23 | *
24 | * @param string $name The view name.
25 | * @param ViewInterface $view The view.
26 | */
27 | public function set($name, ViewInterface $view);
28 |
29 | /**
30 | * Returns whether a view exists or not.
31 | *
32 | * @param string $name The name.
33 | *
34 | * @return boolean Whether a view exists or not.
35 | */
36 | public function has($name);
37 |
38 | /**
39 | * Adds views.
40 | *
41 | * @param array $views An array of views.
42 | */
43 | public function add(array $views);
44 |
45 | /**
46 | * Returns a view.
47 | *
48 | * @param string $name The name.
49 | *
50 | * @return ViewInterface The view.
51 | *
52 | * @throws \InvalidArgumentException If the view does not exist.
53 | */
54 | public function get($name);
55 |
56 | /**
57 | * Returns all the views.
58 | *
59 | * @return array The views.
60 | */
61 | public function all();
62 |
63 | /**
64 | * Removes a view.
65 | *
66 | * @param string $name The name.
67 | *
68 | * @throws \InvalidArgumentException If the view does not exist.
69 | */
70 | public function remove($name);
71 |
72 | /**
73 | * Clears the views.
74 | */
75 | public function clear();
76 | }
77 |
--------------------------------------------------------------------------------
/src/Pagerfanta/View/ViewInterface.php:
--------------------------------------------------------------------------------
1 |
7 | *
8 | * For the full copyright and license information, please view the LICENSE
9 | * file that was distributed with this source code.
10 | */
11 |
12 | namespace Pagerfanta\View;
13 |
14 | use Pagerfanta\PagerfantaInterface;
15 |
16 | /**
17 | * ViewInterface.
18 | *
19 | * @author Pablo Díez
20 | */
21 | interface ViewInterface
22 | {
23 | /**
24 | * Renders a pagerfanta.
25 | *
26 | * The route generator can be any callable to generate
27 | * the routes receiving the page number as first and
28 | * unique argument.
29 | *
30 | * @param PagerfantaInterface $pagerfanta A pagerfanta.
31 | * @param callable $routeGenerator A callable to generate the routes.
32 | * @param array $options An array of options (optional).
33 | */
34 | public function render(PagerfantaInterface $pagerfanta, $routeGenerator, array $options = array());
35 |
36 | /**
37 | * Returns the canonical name.
38 | *
39 | * @return string The canonical name.
40 | */
41 | public function getName();
42 | }
43 |
--------------------------------------------------------------------------------