├── .coveralls.yml ├── .editorconfig ├── .gitignore ├── .php_cs.dist ├── .scrutinizer.yml ├── .styleci.yml ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── docs ├── base_usage.md ├── custom_view.md ├── from_qb.md ├── from_request.md ├── from_request_and_qb.md ├── page_range.md ├── pagination_page_1.png ├── pagination_page_5.png ├── pagination_page_9.png └── parameter_name.md ├── phpstan.neon.dist ├── phpunit.xml.dist ├── src ├── DependencyInjection │ ├── Configuration.php │ └── GpsLabPaginationExtension.php ├── Entity │ └── Node.php ├── Exception │ ├── IncorrectPageNumberException.php │ └── OutOfRangeException.php ├── GpsLabPaginationBundle.php ├── ParamConverter │ └── PaginationParamConverter.php ├── Resources │ ├── config │ │ └── services.yml │ ├── translations │ │ ├── messages.en.yml │ │ └── messages.ru.yml │ └── views │ │ └── pagination.html.twig ├── Service │ ├── Builder.php │ ├── Configuration.php │ ├── NavigateRange.php │ └── View.php └── Twig │ └── Extension │ └── PaginationExtension.php └── tests ├── DependencyInjection └── GpsLabPaginationExtensionTest.php ├── Entity └── NodeTest.php ├── Exception ├── IncorrectPageNumberExceptionTest.php └── OutOfRangeExceptionTest.php ├── GpsLabPaginationBundleTest.php ├── ParamConverter └── PaginationParamConverterTest.php ├── Service ├── BuilderTest.php ├── ConfigurationTest.php ├── NavigateRangeTest.php └── ViewTest.php ├── TestCase.php ├── Twig └── Extension │ └── PaginationExtensionTest.php └── bootstrap.php /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | coverage_clover: build/coverage-clover.xml 3 | json_path: build/coveralls-upload.json 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; top-most EditorConfig file 2 | root = true 3 | 4 | ; Unix-style newlines 5 | [*] 6 | end_of_line = LF 7 | 8 | [*.php] 9 | indent_style = space 10 | indent_size = 4 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor/ 2 | /build/ 3 | phpunit.xml 4 | composer.lock 5 | .php_cs 6 | .php_cs.cache 7 | phpstan.neon -------------------------------------------------------------------------------- /.php_cs.dist: -------------------------------------------------------------------------------- 1 | 6 | @copyright Copyright (c) 2011, Peter Gribanov 7 | @license http://opensource.org/licenses/MIT 8 | EOF; 9 | 10 | return PhpCsFixer\Config::create() 11 | ->setRules([ 12 | '@Symfony' => true, 13 | 'header_comment' => [ 14 | 'comment_type' => 'PHPDoc', 15 | 'header' => $header, 16 | ], 17 | 'array_syntax' => ['syntax' => 'short'], 18 | 'no_superfluous_phpdoc_tags' => false, 19 | 'yoda_style' => false, 20 | 'ordered_imports' => [ 21 | 'sort_algorithm' => 'alpha', 22 | ], 23 | ]) 24 | ->setFinder( 25 | PhpCsFixer\Finder::create() 26 | ->in(__DIR__.'/src') 27 | ->in(__DIR__.'/tests') 28 | ->notPath('bootstrap.php') 29 | ) 30 | ; 31 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | imports: 2 | - php 3 | 4 | tools: 5 | external_code_coverage: 6 | timeout: 600 7 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: symfony 2 | 3 | disabled: 4 | - yoda_style 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | os: linux 4 | 5 | branches: 6 | except: 7 | - /^analysis-.*$/ 8 | 9 | cache: 10 | directories: 11 | - $HOME/.composer/cache 12 | 13 | before_install: 14 | - if [ -n "$GH_TOKEN" ]; then composer config github-oauth.github.com ${GH_TOKEN}; fi; 15 | - if [ -n "$SYMFONY_VERSION" ]; then composer require "symfony/symfony:${SYMFONY_VERSION}" --dev --no-update; fi; 16 | - if [ -n "$DOCTRINE_VERSION" ]; then composer require "doctrine/orm:${DOCTRINE_VERSION}" --dev --no-update; fi; 17 | - if [ -n "$TWIG_VERSION" ]; then composer require "twig/twig:${TWIG_VERSION}" --dev --no-update; fi; 18 | - if [ -n "$SENSIO_FRAMEWORK" ]; then composer require "sensio/framework-extra-bundle:${SENSIO_FRAMEWORK}" --dev --no-update; fi; 19 | - if [ -n "$PHPUNIT_VERSION" ]; then composer require "phpunit/phpunit:${PHPUNIT_VERSION}" --dev --no-update; fi; 20 | - if [ -n "$PHPSTAN_VERSION" ]; then composer require "phpstan/phpstan:${PHPSTAN_VERSION}" --dev --no-update; fi; 21 | 22 | install: COMPOSER_MEMORY_LIMIT=-1 composer install --prefer-dist --no-interaction --no-scripts --no-progress 23 | 24 | script: 25 | - vendor/bin/phpunit --coverage-clover build/coverage-clover.xml 26 | - wget https://scrutinizer-ci.com/ocular.phar 27 | - wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.2.0/php-coveralls.phar 28 | - php ocular.phar code-coverage:upload --format=php-clover build/coverage-clover.xml 29 | - php php-coveralls.phar -v -c .coveralls.yml 30 | 31 | jobs: 32 | include: 33 | - stage: Test 34 | php: 5.5 35 | dist: trusty 36 | 37 | - stage: Test 38 | php: 5.6 39 | 40 | - stage: Test 41 | php: 7.0 42 | 43 | - stage: Test 44 | php: 7.1 45 | 46 | - stage: Test 47 | env: PHPUNIT_VERSION=6.5.* 48 | php: 7.2 49 | 50 | - stage: Test 51 | env: PHPUNIT_VERSION=6.5.* 52 | php: 7.3 53 | 54 | - stage: Test Symfony 55 | env: SYMFONY_VERSION=2.8.* 56 | php: 5.5 57 | dist: trusty 58 | 59 | - stage: Test Symfony 60 | env: SYMFONY_VERSION=3.4.* 61 | php: 5.5 62 | dist: trusty 63 | 64 | - stage: Test Symfony 65 | env: SYMFONY_VERSION=4.4.* PHPUNIT_VERSION=6.5.* 66 | php: 7.1 67 | 68 | - stage: Test Symfony 69 | env: SYMFONY_VERSION=5.0.* PHPUNIT_VERSION=6.5.* 70 | php: 7.2 71 | 72 | # Temporary not test Doctrine 2.4 because composer time out of execution 73 | # - stage: Test Doctrine 74 | # env: DOCTRINE_VERSION=2.4.* 75 | # php: 5.5 76 | # dist: trusty 77 | 78 | - stage: Test Doctrine 79 | env: DOCTRINE_VERSION=2.5.* 80 | php: 5.5 81 | dist: trusty 82 | 83 | - stage: Test Doctrine 84 | env: DOCTRINE_VERSION=2.6.* 85 | php: 7.1 86 | 87 | - stage: Test Twig 88 | env: TWIG_VERSION=1.* 89 | php: 5.5 90 | dist: trusty 91 | 92 | - stage: Test Twig 93 | env: TWIG_VERSION=2.* 94 | php: 7.0 95 | 96 | - stage: Test Twig 97 | env: TWIG_VERSION=3.* PHPUNIT_VERSION=6.5.* 98 | php: 7.2 99 | 100 | - stage: Test SensioFrameworkExtraBundle 101 | env: SENSIO_FRAMEWORK=3.* 102 | php: 5.5 103 | dist: trusty 104 | 105 | - stage: Test SensioFrameworkExtraBundle 106 | env: SENSIO_FRAMEWORK=4.* 107 | php: 5.5 108 | dist: trusty 109 | 110 | - stage: Test SensioFrameworkExtraBundle 111 | env: SENSIO_FRAMEWORK=5.0.* 112 | php: 5.5 113 | dist: trusty 114 | 115 | - stage: Test SensioFrameworkExtraBundle 116 | env: SENSIO_FRAMEWORK=5.1.* 117 | php: 5.5 118 | dist: trusty 119 | 120 | - stage: Test SensioFrameworkExtraBundle 121 | env: SENSIO_FRAMEWORK=5.2.* 122 | php: 5.5 123 | dist: trusty 124 | 125 | - stage: Test SensioFrameworkExtraBundle 126 | env: SENSIO_FRAMEWORK=5.3.* 127 | php: 7.1 128 | dist: trusty 129 | 130 | - stage: Code Quality 131 | name: PHP CS Fixer 132 | before_script: wget https://cs.symfony.com/download/php-cs-fixer-v2.phar -O php-cs-fixer 133 | script: php php-cs-fixer fix --diff --dry-run -v 134 | 135 | - stage: Code Quality 136 | name: PHPStan 137 | php: 7.2 138 | env: PHPSTAN_VERSION=0.11.* 139 | script: vendor/bin/phpstan analyse 140 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2011 GPS Lab 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://packagist.org/packages/gpslab/pagination-bundle) 2 | [](https://packagist.org/packages/gpslab/pagination-bundle) 3 | [](https://travis-ci.org/gpslab/pagination-bundle) 4 | [](https://coveralls.io/github/gpslab/pagination-bundle?branch=master) 5 | [](https://scrutinizer-ci.com/g/gpslab/pagination-bundle/?branch=master) 6 | [](https://styleci.io/repos/86694387) 7 | [](https://github.com/gpslab/pagination-bundle) 8 | 9 | # PaginationBundle 10 | 11 |  12 | 13 |  14 | 15 |  16 | 17 | ## Installation 18 | 19 | Pretty simple with [Composer](http://packagist.org), run: 20 | 21 | ```sh 22 | composer req gpslab/pagination-bundle 23 | ``` 24 | 25 | ## Configuration 26 | 27 | Default configuration 28 | 29 | ```yaml 30 | gpslab_pagination: 31 | # Page range used in pagination control 32 | max_navigate: 5 33 | 34 | # Name of URL parameter for page number 35 | parameter_name: 'page' 36 | 37 | # Sliding pagination controls template 38 | template: 'GpsLabPaginationBundle::pagination.html.twig' 39 | ``` 40 | 41 | ## Simple usage 42 | 43 | ```php 44 | use GpsLab\Bundle\PaginationBundle\Service\Configuration; 45 | 46 | class ArticleController extends Controller 47 | { 48 | private const ARTICLES_PER_PAGE = 30; 49 | 50 | public function index(ArticleRepository $rep, Configuration $pagination): Response 51 | { 52 | $pagination->setTotalPages(ceil($rep->getTotalPublished() / self::ARTICLES_PER_PAGE)); 53 | 54 | // get articles chunk 55 | $offset = ($pagination->getCurrentPage() - 1) * self::ARTICLES_PER_PAGE; 56 | $articles = $rep->getPublished(self::ARTICLES_PER_PAGE, $offset); 57 | 58 | return $this->render('AcmeDemoBundle:Article:index.html.twig', [ 59 | 'articles' => $articles, 60 | 'pagination' => $pagination 61 | ]); 62 | } 63 | } 64 | ``` 65 | 66 | Display pagination in template: 67 | 68 | ```twig 69 | 72 | ``` 73 | 74 | ## Documentation 75 | 76 | * [Base usage](docs/base_usage.md) 77 | * [From QueryBuilder](docs/from_qb.md) 78 | * [From HTTP request](docs/from_request.md) 79 | * [From HTTP request and QueryBuilder](docs/from_request_and_qb.md) 80 | * [Request parameter name](docs/parameter_name.md) 81 | * [Navigation pages range](docs/page_range.md) 82 | * [Custom view](docs/custom_view.md) 83 | 84 | ## License 85 | 86 | This bundle is under the [MIT license](http://opensource.org/licenses/MIT). See the complete license in the file: LICENSE 87 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gpslab/pagination-bundle", 3 | "license": "MIT", 4 | "type": "symfony-bundle", 5 | "description": "Pagination bundle", 6 | "keywords": ["php", "pagination", "symfony", "doctrine"], 7 | "homepage": "http://github.com/gpslab/pagination-bundle", 8 | "autoload": { 9 | "psr-4": { 10 | "GpsLab\\Bundle\\PaginationBundle\\": "src/" 11 | } 12 | }, 13 | "autoload-dev": { 14 | "psr-4": { 15 | "GpsLab\\Bundle\\PaginationBundle\\Tests\\": "tests/" 16 | } 17 | }, 18 | "require": { 19 | "php": ">=5.4.0", 20 | "symfony/http-kernel": "~2.3|~3.0|~4.0|~5.0", 21 | "symfony/dependency-injection": "~2.3|~3.0|~4.0|~5.0", 22 | "symfony/expression-language": "~2.3|~3.0|~4.0|~5.0", 23 | "symfony/config": "~2.3|~3.0|~4.0|~5.0", 24 | "symfony/routing": "~2.3|~3.0|~4.0|~5.0", 25 | "symfony/yaml": "~2.3|~3.0|~4.0|~5.0", 26 | "sensio/framework-extra-bundle": "~3.0|~4.0|~5.0", 27 | "doctrine/orm": "~2.4|~2.5|~2.6", 28 | "twig/twig": "^1.34|^2.0|^3.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^4.8.36" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/base_usage.md: -------------------------------------------------------------------------------- 1 | Base usage 2 | ========== 3 | 4 | ```php 5 | namespace Acme\DemoBundle\Controller; 6 | 7 | use Acme\DemoBundle\Entity\Article; 8 | use Symfony\Bundle\FrameworkBundle\Controller\Controller; 9 | use Symfony\Component\HttpFoundation\Request; 10 | use Symfony\Component\HttpFoundation\Response; 11 | use Sensio\Bundle\FrameworkExtraBundle\Configuration; 12 | 13 | class ArticleController extends Controller 14 | { 15 | /** 16 | * Articles per page. 17 | */ 18 | private const PER_PAGE = 100; 19 | 20 | /** 21 | * @Configuration\Route("/article/", name="article_index") 22 | * @Configuration\Method({"GET"}) 23 | */ 24 | public function index(Request $request): Response 25 | { 26 | $router = $this->get('router'); 27 | 28 | // get total articles 29 | $total = (int) $this-> 30 | ->getDoctrine() 31 | ->createQueryBuilder() 32 | ->select('COUNT(*)') 33 | ->from(Article::class, 'a') 34 | ->getQuery() 35 | ->getSingleScalarResult() 36 | ; 37 | 38 | // build pagination 39 | $pagination = $this 40 | ->get('pagination') 41 | ->paginate( 42 | ceil($total / self::PER_PAGE), // total pages 43 | $request->query->get('page') // correct page 44 | ) 45 | // template of link to page 46 | // character "%d" is replaced by the page number 47 | // you don't need to customize the template, because default template is "?page=%d" 48 | ->setPageLink('/article/?page=%d') 49 | // link for first page 50 | // as a default used the page link template 51 | ->setFirstPageLink('/article/') 52 | ; 53 | 54 | // get articles chunk 55 | $articles = $this-> 56 | ->getDoctrine() 57 | ->createQueryBuilder() 58 | ->select('*') 59 | ->from(Article::class, 'a') 60 | ->setFirstResult(($pagination->getCurrentPage() - 1) * self::PER_PAGE) 61 | ->setMaxResults(self::PER_PAGE) 62 | ->getQuery() 63 | ->getResult() 64 | ; 65 | 66 | return $this->render('AcmeDemoBundle:Article:index.html.twig', [ 67 | 'total' => $total, 68 | 'articles' => $articles, 69 | 'pagination' => $pagination 70 | ]); 71 | } 72 | } 73 | ``` 74 | -------------------------------------------------------------------------------- /docs/custom_view.md: -------------------------------------------------------------------------------- 1 | Custom view 2 | =========== 3 | 4 | You can customize presents pagination. 5 | 6 | You can change template for all pagination on your project from config: 7 | 8 | ```yaml 9 | gpslab_pagination: 10 | # sliding pagination controls template 11 | template: 'custom_pagination.html.twig' 12 | ``` 13 | 14 | Or you can change template for concrete pagination: 15 | 16 | ```twig 17 | {# display navigation #} 18 | {{ pagination_render(pagination, 'custom_pagination.html.twig', {custom_var: 'foo'}) }} 19 | ``` 20 | 21 | Example [Material Design](https://material.io/guidelines/) template for pagination: 22 | 23 | ```twig 24 | {# custom_pagination.html.twig #} 25 | 26 | {# print 'foo' #} 27 | {{ custom_var }} 28 | 29 | {% if pagination.total > 1 %} 30 | {% spaceless %} 31 |
155 | * function ($number) {
156 | * return 'page_'.$number.'.html';
157 | * }
158 | *
159 | *
160 | * @param string|callable $page_link
161 | *
162 | * @return self
163 | */
164 | public function setPageLink($page_link)
165 | {
166 | $this->page_link = $page_link;
167 |
168 | return $this;
169 | }
170 |
171 | /**
172 | * @return string
173 | */
174 | public function getFirstPageLink()
175 | {
176 | return $this->first_page_link;
177 | }
178 |
179 | /**
180 | * @param string $first_page_link
181 | *
182 | * @return self
183 | */
184 | public function setFirstPageLink($first_page_link)
185 | {
186 | $this->first_page_link = $first_page_link;
187 |
188 | return $this;
189 | }
190 |
191 | /**
192 | * @return View
193 | */
194 | public function getView()
195 | {
196 | if (!$this->view) {
197 | $this->view = new View($this, new NavigateRange($this));
198 | }
199 |
200 | return $this->view;
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/src/Service/NavigateRange.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Service;
12 |
13 | class NavigateRange
14 | {
15 | /**
16 | * @var Configuration
17 | */
18 | private $config;
19 |
20 | /**
21 | * @var int
22 | */
23 | private $left_offset = -1;
24 |
25 | /**
26 | * @var int
27 | */
28 | private $right_offset = -1;
29 |
30 | /**
31 | * @param Configuration $config
32 | */
33 | public function __construct(Configuration $config)
34 | {
35 | $this->config = $config;
36 | }
37 |
38 | /**
39 | * @return int
40 | */
41 | public function getLeftOffset()
42 | {
43 | return $this->buildOffset()->left_offset;
44 | }
45 |
46 | /**
47 | * @return int
48 | */
49 | public function getRightOffset()
50 | {
51 | return $this->buildOffset()->right_offset;
52 | }
53 |
54 | /**
55 | * @return self
56 | */
57 | private function buildOffset()
58 | {
59 | if ($this->left_offset < 0) {
60 | $this->definitionOffset();
61 | $this->adjustmentLargeLeftOffset();
62 | $this->adjustmentLargeRightOffset();
63 | $this->adjustmentLowerLeftOffset();
64 | }
65 |
66 | return $this;
67 | }
68 |
69 | /**
70 | * Definition of offset to the left and to the right of the selected page.
71 | */
72 | private function definitionOffset()
73 | {
74 | $this->left_offset = (int) floor(($this->config->getMaxNavigate() - 1) / 2);
75 | $this->right_offset = (int) ceil(($this->config->getMaxNavigate() - 1) / 2);
76 | }
77 |
78 | /**
79 | * Adjustment, if the offset is too large left.
80 | */
81 | private function adjustmentLargeLeftOffset()
82 | {
83 | if ($this->config->getCurrentPage() - $this->left_offset < 1) {
84 | $offset = abs($this->config->getCurrentPage() - 1 - $this->left_offset);
85 | $this->left_offset -= $offset;
86 | $this->right_offset += $offset;
87 | }
88 | }
89 |
90 | /**
91 | * Adjustment, if the offset is too large right.
92 | */
93 | private function adjustmentLargeRightOffset()
94 | {
95 | if ($this->config->getCurrentPage() + $this->right_offset > $this->config->getTotalPages()) {
96 | $offset = abs(
97 | $this->config->getTotalPages() -
98 | $this->config->getCurrentPage() -
99 | $this->right_offset
100 | );
101 | $this->left_offset += $offset;
102 | $this->right_offset -= $offset;
103 | }
104 | }
105 |
106 | /**
107 | * Left offset should point not lower of the first page.
108 | */
109 | private function adjustmentLowerLeftOffset()
110 | {
111 | if ($this->left_offset >= $this->config->getCurrentPage()) {
112 | $this->left_offset = $this->config->getCurrentPage() - 1;
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/src/Service/View.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Service;
12 |
13 | use Doctrine\Common\Collections\ArrayCollection;
14 | use GpsLab\Bundle\PaginationBundle\Entity\Node;
15 |
16 | class View implements \IteratorAggregate
17 | {
18 | /**
19 | * @var Configuration
20 | */
21 | private $config;
22 |
23 | /**
24 | * @var NavigateRange
25 | */
26 | private $range;
27 |
28 | /**
29 | * @var Node|null
30 | */
31 | private $first;
32 |
33 | /**
34 | * @var Node|null
35 | */
36 | private $prev;
37 |
38 | /**
39 | * @var Node|null
40 | */
41 | private $current;
42 |
43 | /**
44 | * @var Node|null
45 | */
46 | private $next;
47 |
48 | /**
49 | * @var Node|null
50 | */
51 | private $last;
52 |
53 | /**
54 | * @var ArrayCollection|null
55 | */
56 | private $list;
57 |
58 | /**
59 | * @param Configuration $config
60 | * @param NavigateRange $range
61 | */
62 | public function __construct(Configuration $config, NavigateRange $range)
63 | {
64 | $this->config = $config;
65 | $this->range = $range;
66 | }
67 |
68 | /**
69 | * @return int
70 | */
71 | public function getTotal()
72 | {
73 | return $this->config->getTotalPages();
74 | }
75 |
76 | /**
77 | * @return Node|null
78 | */
79 | public function getFirst()
80 | {
81 | if (!$this->first && $this->config->getCurrentPage() > 1) {
82 | $this->first = new Node(1, $this->buildLink(1));
83 | }
84 |
85 | return $this->first;
86 | }
87 |
88 | /**
89 | * @return Node|null
90 | */
91 | public function getPrev()
92 | {
93 | if (!$this->prev && $this->config->getCurrentPage() > 1) {
94 | $this->prev = new Node(
95 | $this->config->getCurrentPage() - 1,
96 | $this->buildLink($this->config->getCurrentPage() - 1)
97 | );
98 | }
99 |
100 | return $this->prev;
101 | }
102 |
103 | /**
104 | * @return Node
105 | */
106 | public function getCurrent()
107 | {
108 | if (!$this->current) {
109 | $this->current = new Node(
110 | $this->config->getCurrentPage(),
111 | $this->buildLink($this->config->getCurrentPage()),
112 | true
113 | );
114 | }
115 |
116 | return $this->current;
117 | }
118 |
119 | /**
120 | * @return Node|null
121 | */
122 | public function getNext()
123 | {
124 | if (!$this->next && $this->config->getCurrentPage() < $this->getTotal()) {
125 | $this->next = new Node(
126 | $this->config->getCurrentPage() + 1,
127 | $this->buildLink($this->config->getCurrentPage() + 1)
128 | );
129 | }
130 |
131 | return $this->next;
132 | }
133 |
134 | /**
135 | * @return Node|null
136 | */
137 | public function getLast()
138 | {
139 | if (!$this->last && $this->config->getCurrentPage() < $this->getTotal()) {
140 | $this->last = new Node($this->getTotal(), $this->buildLink($this->getTotal()));
141 | }
142 |
143 | return $this->last;
144 | }
145 |
146 | /**
147 | * @return ArrayCollection|Node[]
148 | */
149 | public function getIterator()
150 | {
151 | if (!($this->list instanceof ArrayCollection)) {
152 | $this->list = new ArrayCollection();
153 |
154 | if ($this->getTotal() > 1) {
155 | // determining the first and last pages in paging based on the current page and offset
156 | $page = $this->config->getCurrentPage() - $this->range->getLeftOffset();
157 | $page_to = $this->config->getCurrentPage() + $this->range->getRightOffset();
158 |
159 | while ($page <= $page_to) {
160 | $this->list->add(new Node(
161 | $page,
162 | $this->buildLink($page),
163 | $page === $this->config->getCurrentPage()
164 | ));
165 | ++$page;
166 | }
167 | }
168 | }
169 |
170 | return $this->list;
171 | }
172 |
173 | /**
174 | * @param int $page
175 | *
176 | * @return string
177 | */
178 | private function buildLink($page)
179 | {
180 | if ($page === 1 && $this->config->getFirstPageLink()) {
181 | return $this->config->getFirstPageLink();
182 | }
183 |
184 | if (is_callable($this->config->getPageLink())) {
185 | return call_user_func($this->config->getPageLink(), $page);
186 | }
187 |
188 | return sprintf($this->config->getPageLink(), $page);
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/src/Twig/Extension/PaginationExtension.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Twig\Extension;
12 |
13 | use GpsLab\Bundle\PaginationBundle\Service\Configuration;
14 | use Twig\Environment;
15 | use Twig\Extension\AbstractExtension;
16 | use Twig\TwigFunction;
17 |
18 | class PaginationExtension extends AbstractExtension
19 | {
20 | /**
21 | * @var string
22 | */
23 | private $template;
24 |
25 | /**
26 | * @param string $template
27 | */
28 | public function __construct($template)
29 | {
30 | $this->template = $template;
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | public function getFunctions()
37 | {
38 | return [
39 | new TwigFunction(
40 | 'pagination_render',
41 | [$this, 'renderPagination'],
42 | ['is_safe' => ['html'], 'needs_environment' => true]
43 | ),
44 | ];
45 | }
46 |
47 | /**
48 | * @param \Twig\Environment $env
49 | * @param Configuration $pagination
50 | * @param string $template
51 | * @param array $view_params
52 | * @param int $max_navigate
53 | *
54 | * @throws \Twig\Error\LoaderError
55 | * @throws \Twig\Error\RuntimeError
56 | * @throws \Twig\Error\SyntaxError
57 | *
58 | * @return string
59 | */
60 | public function renderPagination(
61 | Environment $env,
62 | Configuration $pagination,
63 | $template = null,
64 | array $view_params = [],
65 | $max_navigate = 0
66 | ) {
67 | if ($max_navigate > 0) {
68 | // not change original object
69 | $new_pagination = clone $pagination;
70 | $new_pagination->setMaxNavigate($max_navigate);
71 |
72 | $pagination_view = $new_pagination->getView();
73 | } else {
74 | $pagination_view = $pagination->getView();
75 | }
76 |
77 | return $env->render(
78 | $template ?: $this->template,
79 | array_merge($view_params, ['pagination' => $pagination_view])
80 | );
81 | }
82 |
83 | /**
84 | * @return string
85 | */
86 | public function getName()
87 | {
88 | return 'gpslab_pagination_extension';
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/DependencyInjection/GpsLabPaginationExtensionTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\DependencyInjection;
12 |
13 | use GpsLab\Bundle\PaginationBundle\DependencyInjection\GpsLabPaginationExtension;
14 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
15 | use Symfony\Component\DependencyInjection\ContainerBuilder;
16 |
17 | class GpsLabPaginationExtensionTest extends TestCase
18 | {
19 | /**
20 | * @var GpsLabPaginationExtension
21 | */
22 | private $extension;
23 |
24 | protected function setUp()
25 | {
26 | $this->extension = new GpsLabPaginationExtension();
27 | }
28 |
29 | public function testLoad()
30 | {
31 | $container = new ContainerBuilder();
32 | $this->extension->load([], $container);
33 |
34 | self::assertEquals(5, $container->getParameter('pagination.max_navigate'));
35 | self::assertEquals('page', $container->getParameter('pagination.parameter_name'));
36 | self::assertEquals(
37 | 'GpsLabPaginationBundle::pagination.html.twig',
38 | $container->getParameter('pagination.template')
39 | );
40 | }
41 |
42 | public function testGetAlias()
43 | {
44 | self::assertEquals('gpslab_pagination', $this->extension->getAlias());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/Entity/NodeTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Entity;
12 |
13 | use GpsLab\Bundle\PaginationBundle\Entity\Node;
14 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
15 |
16 | class NodeTest extends TestCase
17 | {
18 | /**
19 | * @return array
20 | */
21 | public function getNodes()
22 | {
23 | return [
24 | [1, '', false],
25 | [4, 'http://example.com/?p=4', true],
26 | ];
27 | }
28 |
29 | /**
30 | * @dataProvider getNodes
31 | *
32 | * @param int $page
33 | * @param string $link
34 | * @param bool $is_current
35 | */
36 | public function test($page, $link, $is_current)
37 | {
38 | $node = new Node($page, $link, $is_current);
39 | self::assertEquals($page, $node->getPage());
40 | self::assertEquals($link, $node->getLink());
41 | self::assertEquals($is_current, $node->isCurrent());
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tests/Exception/IncorrectPageNumberExceptionTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Exception;
12 |
13 | use GpsLab\Bundle\PaginationBundle\Exception\IncorrectPageNumberException;
14 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
15 |
16 | class IncorrectPageNumberExceptionTest extends TestCase
17 | {
18 | public function testIncorrect()
19 | {
20 | $current_page = -5;
21 | $message = sprintf('Incorrect "%s" page number.', $current_page);
22 |
23 | $exception = IncorrectPageNumberException::incorrect($current_page);
24 |
25 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Exception\IncorrectPageNumberException', $exception);
26 | self::assertInstanceOf('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', $exception);
27 | self::assertEquals($message, $exception->getMessage());
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/Exception/OutOfRangeExceptionTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Exception;
12 |
13 | use GpsLab\Bundle\PaginationBundle\Exception\OutOfRangeException;
14 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
15 |
16 | class OutOfRangeExceptionTest extends TestCase
17 | {
18 | public function testIncorrect()
19 | {
20 | $current_page = -5;
21 | $total_pages = 10;
22 | $message = sprintf('Select page "%s" is out of range "%s".', $current_page, $total_pages);
23 |
24 | $exception = OutOfRangeException::out($current_page, $total_pages);
25 |
26 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Exception\OutOfRangeException', $exception);
27 | self::assertInstanceOf('Symfony\Component\HttpKernel\Exception\NotFoundHttpException', $exception);
28 | self::assertEquals($message, $exception->getMessage());
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/GpsLabPaginationBundleTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests;
12 |
13 | use GpsLab\Bundle\PaginationBundle\GpsLabPaginationBundle;
14 |
15 | class GpsLabPaginationBundleTest extends TestCase
16 | {
17 | public function testGetContainerExtension()
18 | {
19 | $bundle = new GpsLabPaginationBundle();
20 | $extension = $bundle->getContainerExtension();
21 |
22 | self::assertInstanceOf(
23 | 'GpsLab\Bundle\PaginationBundle\DependencyInjection\GpsLabPaginationExtension',
24 | $extension
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/ParamConverter/PaginationParamConverterTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\ParamConverter;
12 |
13 | use GpsLab\Bundle\PaginationBundle\ParamConverter\PaginationParamConverter;
14 | use GpsLab\Bundle\PaginationBundle\Service\Configuration;
15 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
16 | use PHPUnit\Framework\MockObject\MockObject;
17 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
18 | use Symfony\Component\HttpFoundation\Request;
19 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
20 | use Symfony\Component\Routing\RouterInterface;
21 |
22 | class PaginationParamConverterTest extends TestCase
23 | {
24 | const MAX_NAVIGATE = 10;
25 |
26 | const PARAMETER_NAME = 'p';
27 |
28 | /**
29 | * @var RouterInterface|\PHPUnit_Framework_MockObject_MockObject|MockObject
30 | */
31 | private $router;
32 |
33 | /**
34 | * @var ParamConverter|\PHPUnit_Framework_MockObject_MockObject|MockObject
35 | */
36 | private $configuration;
37 |
38 | /**
39 | * @var PaginationParamConverter
40 | */
41 | private $converter;
42 |
43 | protected function setUp()
44 | {
45 | $this->router = $this->getMockNoConstructor('Symfony\Component\Routing\RouterInterface');
46 | $this->configuration = $this->getMockNoConstructor('Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter');
47 |
48 | $this->converter = new PaginationParamConverter($this->router, self::MAX_NAVIGATE, self::PARAMETER_NAME);
49 | }
50 |
51 | public function testSupports()
52 | {
53 | $this->configuration
54 | ->expects(self::once())
55 | ->method('getClass')
56 | ->willReturn('GpsLab\Bundle\PaginationBundle\Service\Configuration');
57 |
58 | self::assertTrue($this->converter->supports($this->configuration));
59 | }
60 |
61 | public function testNotSupports()
62 | {
63 | $this->configuration
64 | ->expects(self::once())
65 | ->method('getClass')
66 | ->willReturn('stdClass');
67 |
68 | self::assertFalse($this->converter->supports($this->configuration));
69 | }
70 |
71 | /**
72 | * @return array
73 | */
74 | public function getOptions()
75 | {
76 | return [
77 | [
78 | [],
79 | [],
80 | [],
81 | self::MAX_NAVIGATE,
82 | self::PARAMETER_NAME,
83 | UrlGeneratorInterface::ABSOLUTE_PATH,
84 | ],
85 | [
86 | [
87 | 'max_navigate' => 5,
88 | ],
89 | [],
90 | [],
91 | 5,
92 | self::PARAMETER_NAME,
93 | UrlGeneratorInterface::ABSOLUTE_PATH,
94 | ],
95 | [
96 | [
97 | 'parameter_name' => 'page',
98 | ],
99 | [],
100 | [],
101 | self::MAX_NAVIGATE,
102 | 'page',
103 | UrlGeneratorInterface::ABSOLUTE_PATH,
104 | ],
105 | [
106 | [
107 | 'reference_type' => 'absolute_url',
108 | ],
109 | [],
110 | [],
111 | self::MAX_NAVIGATE,
112 | self::PARAMETER_NAME,
113 | UrlGeneratorInterface::ABSOLUTE_PATH,
114 | ],
115 | [
116 | [
117 | 'reference_type' => 'absolute_path',
118 | ],
119 | [],
120 | [],
121 | self::MAX_NAVIGATE,
122 | self::PARAMETER_NAME,
123 | UrlGeneratorInterface::ABSOLUTE_PATH,
124 | ],
125 | [
126 | [
127 | 'reference_type' => 'relative_path',
128 | ],
129 | [],
130 | [],
131 | self::MAX_NAVIGATE,
132 | self::PARAMETER_NAME,
133 | UrlGeneratorInterface::RELATIVE_PATH,
134 | ],
135 | [
136 | [
137 | 'reference_type' => 'network_path',
138 | ],
139 | [],
140 | [],
141 | self::MAX_NAVIGATE,
142 | self::PARAMETER_NAME,
143 | UrlGeneratorInterface::NETWORK_PATH,
144 | ],
145 | [
146 | [],
147 | [
148 | 'foo' => 'bar',
149 | ],
150 | [],
151 | self::MAX_NAVIGATE,
152 | self::PARAMETER_NAME,
153 | UrlGeneratorInterface::ABSOLUTE_PATH,
154 | ],
155 | [
156 | [],
157 | [],
158 | [
159 | 'foo' => 'bar',
160 | ],
161 | self::MAX_NAVIGATE,
162 | self::PARAMETER_NAME,
163 | UrlGeneratorInterface::ABSOLUTE_PATH,
164 | ],
165 | [
166 | [],
167 | [
168 | 'foo' => 'bar',
169 | ],
170 | [
171 | 'foo' => 'baz',
172 | ],
173 | self::MAX_NAVIGATE,
174 | self::PARAMETER_NAME,
175 | UrlGeneratorInterface::ABSOLUTE_PATH,
176 | ],
177 | [
178 | [],
179 | [
180 | self::PARAMETER_NAME => 'bar',
181 | ],
182 | [
183 | self::PARAMETER_NAME => 'baz',
184 | ],
185 | self::MAX_NAVIGATE,
186 | self::PARAMETER_NAME,
187 | UrlGeneratorInterface::ABSOLUTE_PATH,
188 | ],
189 | ];
190 | }
191 |
192 | /**
193 | * @dataProvider getOptions
194 | *
195 | * @param array $options
196 | * @param array $query
197 | * @param array $route_params
198 | * @param int $max_navigate
199 | * @param string $parameter_name
200 | * @param string $reference_type
201 | */
202 | public function testApply(
203 | array $options,
204 | array $query,
205 | array $route_params,
206 | $max_navigate,
207 | $parameter_name,
208 | $reference_type
209 | ) {
210 | $route = 'my_route';
211 | $prop_name = 'pagination';
212 | $first_page_link = 'first_page_link';
213 | $page_link = 'page_link';
214 | $page_number = 1;
215 |
216 | $expected_route_params = array_merge($query, $route_params);
217 | unset($expected_route_params[$parameter_name]);
218 |
219 | $this->configuration
220 | ->expects(self::once())
221 | ->method('getOptions')
222 | ->willReturn($options);
223 | $this->configuration
224 | ->expects(self::once())
225 | ->method('getName')
226 | ->willReturn($prop_name);
227 |
228 | $this->router
229 | ->expects(self::at(0))
230 | ->method('generate')
231 | ->with($route, $expected_route_params, $reference_type)
232 | ->willReturn($first_page_link);
233 | $this->router
234 | ->expects(self::at(1))
235 | ->method('generate')
236 | ->with($route, [$parameter_name => $page_number] + $expected_route_params, $reference_type)
237 | ->willReturn($page_link);
238 |
239 | $request = new Request($query, [], [
240 | '_route' => $route,
241 | '_route_params' => $route_params,
242 | ]);
243 |
244 | self::assertTrue($this->converter->apply($request, $this->configuration));
245 |
246 | self::assertTrue($request->attributes->has($prop_name));
247 | /* @var $configuration Configuration */
248 | $configuration = $request->attributes->get($prop_name);
249 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Service\Configuration', $configuration);
250 | self::assertSame($max_navigate, $configuration->getMaxNavigate());
251 | self::assertSame($first_page_link, $configuration->getFirstPageLink());
252 | $callable_page_link = $configuration->getPageLink();
253 | self::assertInternalType('callable', $callable_page_link);
254 | self::assertSame($page_link, $callable_page_link($page_number));
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/tests/Service/BuilderTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Service;
12 |
13 | use Doctrine\ORM\AbstractQuery;
14 | use Doctrine\ORM\QueryBuilder;
15 | use GpsLab\Bundle\PaginationBundle\Service\Builder;
16 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
17 | use Symfony\Bundle\FrameworkBundle\Routing\Router;
18 | use Symfony\Component\HttpFoundation\Request;
19 | use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
20 |
21 | class BuilderTest extends TestCase
22 | {
23 | /**
24 | * @var \PHPUnit_Framework_MockObject_MockObject|Router
25 | */
26 | private $router;
27 |
28 | /**
29 | * @var \PHPUnit_Framework_MockObject_MockObject|AbstractQuery
30 | */
31 | private $query;
32 |
33 | /**
34 | * @var \PHPUnit_Framework_MockObject_MockObject|QueryBuilder
35 | */
36 | private $query_builder;
37 |
38 | /**
39 | * @var Request
40 | */
41 | private $request;
42 |
43 | /**
44 | * @var array
45 | */
46 | private $query_params = [
47 | 'foo' => 'bar',
48 | 'p' => 2,
49 | ];
50 |
51 | protected function setUp()
52 | {
53 | $this->router = $this->getMockNoConstructor('Symfony\Bundle\FrameworkBundle\Routing\Router');
54 | $this->query = $this->getMockAbstract('Doctrine\ORM\AbstractQuery', ['getSingleScalarResult']);
55 | $this->query_builder = $this->getMockNoConstructor('Doctrine\ORM\QueryBuilder');
56 |
57 | $this->request = new Request($this->query_params);
58 | }
59 |
60 | public function testDefaultPageLink()
61 | {
62 | $builder = new Builder($this->router, 5, 'p');
63 |
64 | self::assertEquals('?p=%d', $builder->paginate(10, 3)->getPageLink());
65 | }
66 |
67 | /**
68 | * @return array
69 | */
70 | public function getPaginateData()
71 | {
72 | return [
73 | [5, 10, 1],
74 | [10, 150, 33],
75 | ];
76 | }
77 |
78 | /**
79 | * @dataProvider getPaginateData
80 | *
81 | * @param int $max_navigate
82 | * @param int $total_pages
83 | * @param int $current_page
84 | */
85 | public function testPaginate($max_navigate, $total_pages, $current_page)
86 | {
87 | $builder = new Builder($this->router, $max_navigate, 'page');
88 | $config = $builder->paginate($total_pages, $current_page);
89 |
90 | self::assertEquals($max_navigate, $config->getMaxNavigate());
91 | self::assertEquals($total_pages, $config->getTotalPages());
92 | self::assertEquals($current_page, $config->getCurrentPage());
93 | }
94 |
95 | /**
96 | * @return array
97 | */
98 | public function getPaginateQueryData()
99 | {
100 | return [
101 | [5, 5, 10, 1],
102 | [10, 10, 150, 7],
103 | ];
104 | }
105 |
106 | /**
107 | * @dataProvider getPaginateQueryData
108 | *
109 | * @param int $max_navigate
110 | * @param int $per_page
111 | * @param int $total
112 | * @param int $current_page
113 | */
114 | public function testPaginateQuery($max_navigate, $per_page, $total, $current_page)
115 | {
116 | $this->countQuery($total);
117 |
118 | $this->query_builder
119 | ->expects($this->once())
120 | ->method('setFirstResult')
121 | ->with(($current_page - 1) * $per_page)
122 | ->willReturnSelf()
123 | ;
124 | $this->query_builder
125 | ->expects($this->once())
126 | ->method('setMaxResults')
127 | ->with($per_page)
128 | ->willReturnSelf()
129 | ;
130 |
131 | $builder = new Builder($this->router, $max_navigate, 'page');
132 | $config = $builder->paginateQuery($this->query_builder, $per_page, $current_page);
133 |
134 | self::assertEquals($max_navigate, $config->getMaxNavigate());
135 | self::assertEquals(ceil($total / $per_page), $config->getTotalPages());
136 | self::assertEquals($current_page, $config->getCurrentPage());
137 | }
138 |
139 | /**
140 | * @expectedException \GpsLab\Bundle\PaginationBundle\Exception\OutOfRangeException
141 | */
142 | public function testPaginateQueryOutOfRange()
143 | {
144 | $total = 10;
145 | $per_page = 5;
146 | $current_page = 150;
147 |
148 | $this->countQuery($total);
149 |
150 | $builder = new Builder($this->router, 5, 'page');
151 | $builder->paginateQuery($this->query_builder, $per_page, $current_page);
152 | }
153 |
154 | /**
155 | * @expectedException \GpsLab\Bundle\PaginationBundle\Exception\IncorrectPageNumberException
156 | */
157 | public function testPaginateRequestIncorrectPage()
158 | {
159 | $this->request = new Request(array_merge($this->query_params, [
160 | 'page' => 'foo',
161 | ]));
162 |
163 | $builder = new Builder($this->router, 5, 'page');
164 | $builder->paginateRequest($this->request, 10);
165 | }
166 |
167 | /**
168 | * @expectedException \GpsLab\Bundle\PaginationBundle\Exception\OutOfRangeException
169 | */
170 | public function testPaginateRequestLowPageNumber()
171 | {
172 | $this->request = new Request(array_merge($this->query_params, [
173 | 'p' => 0,
174 | ]));
175 |
176 | $builder = new Builder($this->router, 5, 'page');
177 | $builder->paginateRequest($this->request, 10, 'p');
178 | }
179 |
180 | /**
181 | * @expectedException \GpsLab\Bundle\PaginationBundle\Exception\OutOfRangeException
182 | */
183 | public function testPaginateRequestOutOfRange()
184 | {
185 | $this->request = new Request(array_merge($this->query_params, [
186 | 'p' => 150,
187 | ]));
188 |
189 | $builder = new Builder($this->router, 5, 'page');
190 | $builder->paginateRequest($this->request, 10, 'p');
191 | }
192 |
193 | public function testPaginateRequest()
194 | {
195 | $max_navigate = 6;
196 | $total_pages = 10;
197 | $parameter_name = 'p';
198 | $route = '_route';
199 | $route_params = ['foo' => 'baz', '_route_params' => 123];
200 | $all_params = array_merge($this->query_params, $route_params);
201 | $reference_type = UrlGeneratorInterface::ABSOLUTE_URL;
202 | $this->request = new Request(array_merge($this->query_params, [
203 | 'p' => null,
204 | ]), [], [
205 | '_route' => $route,
206 | '_route_params' => $route_params,
207 | ]);
208 |
209 | $that = $this;
210 | $this->router
211 | ->expects($this->atLeastOnce())
212 | ->method('generate')
213 | ->willReturnCallback(function ($_route, $_route_params, $_reference_type) use (
214 | $that,
215 | $route,
216 | $reference_type
217 | ) {
218 | $that->assertEquals($reference_type, $_reference_type);
219 | $that->assertEquals($route, $_route);
220 |
221 | return $_route.http_build_query($_route_params);
222 | })
223 | ;
224 |
225 | $builder = new Builder($this->router, $max_navigate, 'page');
226 | $config = $builder->paginateRequest($this->request, $total_pages, $parameter_name, $reference_type);
227 |
228 | self::assertEquals($max_navigate, $config->getMaxNavigate());
229 | self::assertEquals($total_pages, $config->getTotalPages());
230 | self::assertEquals(1, $config->getCurrentPage());
231 | unset($all_params['p']);
232 | self::assertEquals($route.http_build_query($all_params), $config->getFirstPageLink());
233 | self::assertInstanceOf('Closure', $config->getPageLink());
234 | $page_number = 3;
235 | self::assertEquals(
236 | $route.http_build_query($all_params + [$parameter_name => $page_number]),
237 | call_user_func($config->getPageLink(), $page_number)
238 | );
239 | }
240 |
241 | /**
242 | * @expectedException \GpsLab\Bundle\PaginationBundle\Exception\IncorrectPageNumberException
243 | */
244 | public function testPaginateRequesQuerytIncorrectPage()
245 | {
246 | $this->request = new Request(array_merge($this->query_params, [
247 | 'page' => 'foo',
248 | ]));
249 | $this->countQuery(10);
250 |
251 | $builder = new Builder($this->router, 5, 'page');
252 | $builder->paginateRequestQuery($this->request, $this->query_builder, 5);
253 | }
254 |
255 | /**
256 | * @expectedException \GpsLab\Bundle\PaginationBundle\Exception\OutOfRangeException
257 | */
258 | public function testPaginateRequestQueryLowPageNumber()
259 | {
260 | $this->request = new Request(array_merge($this->query_params, [
261 | 'p' => 0,
262 | ]));
263 | $this->countQuery(10);
264 |
265 | $builder = new Builder($this->router, 5, 'page');
266 | $builder->paginateRequestQuery($this->request, $this->query_builder, 5, 'p');
267 | }
268 |
269 | /**
270 | * @expectedException \GpsLab\Bundle\PaginationBundle\Exception\OutOfRangeException
271 | */
272 | public function testPaginateRequestQueryOutOfRange()
273 | {
274 | $this->request = new Request(array_merge($this->query_params, [
275 | 'p' => 150,
276 | ]));
277 | $this->countQuery(10);
278 |
279 | $builder = new Builder($this->router, 5, 'page');
280 | $builder->paginateRequestQuery($this->request, $this->query_builder, 5, 'p');
281 | }
282 |
283 | public function testPaginateRequestQuery()
284 | {
285 | $per_page = 10;
286 | $current_page = 7;
287 | $max_navigate = 6;
288 | $total = 150;
289 | $parameter_name = 'p';
290 | $route = '_route';
291 | $route_params = ['foo' => 'baz', '_route_params'];
292 | $all_params = array_merge($this->query_params, $route_params);
293 | $reference_type = UrlGeneratorInterface::ABSOLUTE_URL;
294 | $this->request = new Request(array_merge($this->query_params, [
295 | 'p' => $current_page,
296 | ]), [], [
297 | '_route' => $route,
298 | '_route_params' => $route_params,
299 | ]);
300 |
301 | $this->countQuery($total);
302 | $this->query_builder
303 | ->expects($this->once())
304 | ->method('setFirstResult')
305 | ->with(($current_page - 1) * $per_page)
306 | ->willReturnSelf()
307 | ;
308 | $this->query_builder
309 | ->expects($this->once())
310 | ->method('setMaxResults')
311 | ->with($per_page)
312 | ->willReturnSelf()
313 | ;
314 |
315 | $that = $this;
316 | $this->router
317 | ->expects($this->atLeastOnce())
318 | ->method('generate')
319 | ->willReturnCallback(function ($_route, $_route_params, $_reference_type) use (
320 | $that,
321 | $route,
322 | $reference_type
323 | ) {
324 | $that->assertEquals($reference_type, $_reference_type);
325 | $that->assertEquals($route, $_route);
326 |
327 | return $_route.http_build_query($_route_params);
328 | })
329 | ;
330 |
331 | $builder = new Builder($this->router, $max_navigate, 'page');
332 | $config = $builder->paginateRequestQuery(
333 | $this->request,
334 | $this->query_builder,
335 | $per_page,
336 | $parameter_name,
337 | $reference_type
338 | );
339 |
340 | self::assertEquals($max_navigate, $config->getMaxNavigate());
341 | self::assertEquals(ceil($total / $per_page), $config->getTotalPages());
342 | self::assertEquals($current_page, $config->getCurrentPage());
343 | unset($all_params['p']);
344 | self::assertEquals($route.http_build_query($all_params), $config->getFirstPageLink());
345 | self::assertInstanceOf('Closure', $config->getPageLink());
346 | $page_number = 3;
347 | self::assertEquals(
348 | $route.http_build_query($all_params + [$parameter_name => $page_number]),
349 | call_user_func($config->getPageLink(), $page_number)
350 | );
351 | }
352 |
353 | /**
354 | * @param int $total
355 | */
356 | private function countQuery($total)
357 | {
358 | $this->query
359 | ->expects($this->once())
360 | ->method('getSingleScalarResult')
361 | ->willReturn($total);
362 |
363 | $this->query_builder
364 | ->expects($this->once())
365 | ->method('getRootAliases')
366 | ->willReturn(['a', 'b']);
367 | $this->query_builder
368 | ->expects($this->once())
369 | ->method('select')
370 | ->with('COUNT(a)')
371 | ->willReturnSelf();
372 | $this->query_builder
373 | ->expects($this->once())
374 | ->method('getQuery')
375 | ->willReturn($this->query);
376 | }
377 | }
378 |
--------------------------------------------------------------------------------
/tests/Service/ConfigurationTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Service;
12 |
13 | use GpsLab\Bundle\PaginationBundle\Service\Configuration;
14 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
15 |
16 | class ConfigurationTest extends TestCase
17 | {
18 | /**
19 | * @var Configuration
20 | */
21 | private $config;
22 |
23 | protected function setUp()
24 | {
25 | $this->config = new Configuration(150, 33);
26 | }
27 |
28 | public function testDefaultPageLink()
29 | {
30 | self::assertEquals('?page=%d', $this->config->getPageLink());
31 | }
32 |
33 | /**
34 | * @return array
35 | */
36 | public function getConfigs()
37 | {
38 | return [
39 | [10, 1],
40 | [150, 33],
41 | ];
42 | }
43 |
44 | /**
45 | * @dataProvider getConfigs
46 | *
47 | * @param int $total_pages
48 | * @param int $current_page
49 | */
50 | public function testConstruct($total_pages, $current_page)
51 | {
52 | $config = new Configuration($total_pages, $current_page);
53 | self::assertEquals($total_pages, $config->getTotalPages());
54 | self::assertEquals($current_page, $config->getCurrentPage());
55 | }
56 |
57 | /**
58 | * @dataProvider getConfigs
59 | *
60 | * @param int $total_pages
61 | * @param int $current_page
62 | */
63 | public function testCreate($total_pages, $current_page)
64 | {
65 | $config = Configuration::create($total_pages, $current_page);
66 | self::assertEquals($total_pages, $config->getTotalPages());
67 | self::assertEquals($current_page, $config->getCurrentPage());
68 | }
69 |
70 | /**
71 | * @return array
72 | */
73 | public function getMethods()
74 | {
75 | return [
76 | [
77 | 150,
78 | 10,
79 | 'getTotalPages',
80 | 'setTotalPages',
81 | ],
82 | [
83 | 33,
84 | 1,
85 | 'getCurrentPage',
86 | 'setCurrentPage',
87 | ],
88 | [
89 | Configuration::DEFAULT_LIST_LENGTH,
90 | Configuration::DEFAULT_LIST_LENGTH + 5,
91 | 'getMaxNavigate',
92 | 'setMaxNavigate',
93 | ],
94 | [
95 | Configuration::DEFAULT_PAGE_LINK,
96 | 'page_%s.html',
97 | 'getPageLink',
98 | 'setPageLink',
99 | ],
100 | [
101 | Configuration::DEFAULT_PAGE_LINK,
102 | function ($number) {
103 | return 'page_'.$number.'.html';
104 | },
105 | 'getPageLink',
106 | 'setPageLink',
107 | ],
108 | [
109 | '',
110 | '/index.html',
111 | 'getFirstPageLink',
112 | 'setFirstPageLink',
113 | ],
114 | ];
115 | }
116 |
117 | /**
118 | * @dataProvider getMethods
119 | *
120 | * @param mixed $default
121 | * @param mixed $new
122 | * @param string $getter
123 | * @param string $setter
124 | */
125 | public function testSetGet($default, $new, $getter, $setter)
126 | {
127 | self::assertEquals($default, call_user_func([$this->config, $getter]));
128 | self::assertEquals($this->config, call_user_func([$this->config, $setter], $new));
129 | self::assertEquals($new, call_user_func([$this->config, $getter]));
130 | }
131 |
132 | public function testGetView()
133 | {
134 | $view = $this->config->getView();
135 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Service\View', $view);
136 |
137 | // test lazy load
138 | $this->config->setPageLink('?p=%s');
139 | self::assertEquals($view, $this->config->getView());
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/tests/Service/NavigateRangeTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Service;
12 |
13 | use GpsLab\Bundle\PaginationBundle\Service\Configuration;
14 | use GpsLab\Bundle\PaginationBundle\Service\NavigateRange;
15 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
16 |
17 | class NavigateRangeTest extends TestCase
18 | {
19 | /**
20 | * @var \PHPUnit_Framework_MockObject_MockObject|Configuration
21 | */
22 | private $config;
23 |
24 | /**
25 | * @var NavigateRange
26 | */
27 | private $range;
28 |
29 | protected function setUp()
30 | {
31 | $this->config = $this->getMockNoConstructor('GpsLab\Bundle\PaginationBundle\Service\Configuration');
32 |
33 | $this->range = new NavigateRange($this->config);
34 | }
35 |
36 | /**
37 | * @return array
38 | */
39 | public function getOffsets()
40 | {
41 | return [
42 | [5, 1, 2, 0, 1],
43 | [5, 2, 2, 1, 0],
44 | [5, 1, 10, 0, 4],
45 | [5, 2, 10, 1, 3],
46 | [5, 3, 10, 2, 2],
47 | [5, 4, 10, 2, 2],
48 | [5, 8, 10, 2, 2],
49 | [5, 9, 10, 3, 1],
50 | [5, 10, 10, 4, 0],
51 | [5, 1, 1, 0, 0], // list pages is empty
52 | ];
53 | }
54 |
55 | /**
56 | * @dataProvider getOffsets
57 | *
58 | * @param int $max_navigate
59 | * @param int $current_page
60 | * @param int $total_pages
61 | * @param int $left_offset
62 | * @param int $right_offset
63 | */
64 | public function testBuildOffset($max_navigate, $current_page, $total_pages, $left_offset, $right_offset)
65 | {
66 | $this->config
67 | ->expects($this->exactly(2)) // test cache build result
68 | ->method('getMaxNavigate')
69 | ->willReturn($max_navigate);
70 | $this->config
71 | ->expects($this->atLeastOnce())
72 | ->method('getCurrentPage')
73 | ->willReturn($current_page);
74 | $this->config
75 | ->expects($this->atLeastOnce())
76 | ->method('getTotalPages')
77 | ->willReturn($total_pages);
78 |
79 | self::assertEquals($left_offset, $this->range->getLeftOffset());
80 | self::assertEquals($right_offset, $this->range->getRightOffset());
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tests/Service/ViewTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Service;
12 |
13 | use Doctrine\Common\Collections\ArrayCollection;
14 | use GpsLab\Bundle\PaginationBundle\Entity\Node;
15 | use GpsLab\Bundle\PaginationBundle\Service\Configuration;
16 | use GpsLab\Bundle\PaginationBundle\Service\NavigateRange;
17 | use GpsLab\Bundle\PaginationBundle\Service\View;
18 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
19 |
20 | class ViewTest extends TestCase
21 | {
22 | /**
23 | * @var \PHPUnit_Framework_MockObject_MockObject|Configuration
24 | */
25 | private $config;
26 |
27 | /**
28 | * @var \PHPUnit_Framework_MockObject_MockObject|NavigateRange
29 | */
30 | private $range;
31 |
32 | /**
33 | * @var View
34 | */
35 | private $view;
36 |
37 | protected function setUp()
38 | {
39 | $this->config = $this->getMockNoConstructor('GpsLab\Bundle\PaginationBundle\Service\Configuration');
40 | $this->range = $this->getMockNoConstructor('GpsLab\Bundle\PaginationBundle\Service\NavigateRange');
41 |
42 | $this->view = new View($this->config, $this->range);
43 | }
44 |
45 | public function testGetTotal()
46 | {
47 | $this->config
48 | ->expects($this->once())
49 | ->method('getTotalPages')
50 | ->willReturn('110')
51 | ;
52 |
53 | self::assertEquals(110, $this->view->getTotal());
54 | }
55 |
56 | /**
57 | * @return array
58 | */
59 | public function getFailNodes()
60 | {
61 | return [
62 | ['getFirst', 1],
63 | ['getPrev', 1],
64 | ['getNext', 110],
65 | ['getLast', 110],
66 | ];
67 | }
68 |
69 | /**
70 | * @dataProvider getFailNodes
71 | *
72 | * @param string $method
73 | * @param int $current_page
74 | */
75 | public function testGetNodeFail($method, $current_page)
76 | {
77 | $this->config
78 | ->expects($this->any())
79 | ->method('getTotalPages')
80 | ->willReturn(110)
81 | ;
82 | $this->config
83 | ->expects($this->any())
84 | ->method('getCurrentPage')
85 | ->willReturn($current_page)
86 | ;
87 |
88 | self::assertNull(call_user_func([$this->view, $method]));
89 | }
90 |
91 | /**
92 | * @return array
93 | */
94 | public function getPageLinks()
95 | {
96 | return [
97 | ['page_%s.html'],
98 | [function ($number) {
99 | return 'page_'.$number.'.html';
100 | }],
101 | ];
102 | }
103 |
104 | /**
105 | * @return array
106 | */
107 | public function getFirstPageLinks()
108 | {
109 | return [
110 | ['page_%s.html', ''],
111 | ['page_%s.html', '/index.html'],
112 | [function ($number) {
113 | return 'page_'.$number.'.html';
114 | }, ''],
115 | [function ($number) {
116 | return 'page_'.$number.'.html';
117 | }, '/index.html'],
118 | ];
119 | }
120 |
121 | /**
122 | * @param string|callable $page_link
123 | * @param int $number
124 | *
125 | * @return string
126 | */
127 | protected function getLink($page_link, $number)
128 | {
129 | return is_callable($page_link) ? call_user_func($page_link, $number) : sprintf($page_link, $number);
130 | }
131 |
132 | /**
133 | * @dataProvider getFirstPageLinks
134 | *
135 | * @param string|callable $page_link
136 | * @param string $first_page_link
137 | */
138 | public function testGetFirst($page_link, $first_page_link)
139 | {
140 | $this->config
141 | ->expects($this->once())
142 | ->method('getCurrentPage')
143 | ->willReturn(10);
144 | $this->config
145 | ->expects($first_page_link ? $this->atLeastOnce() : $this->once())
146 | ->method('getFirstPageLink')
147 | ->willReturn($first_page_link);
148 | $this->config
149 | ->expects($first_page_link ? $this->never() : $this->atLeastOnce())
150 | ->method('getPageLink')
151 | ->willReturn($page_link);
152 |
153 | $node = $this->view->getFirst();
154 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Entity\Node', $node);
155 | self::assertEquals(1, $node->getPage());
156 | if ($first_page_link) {
157 | self::assertEquals($first_page_link, $node->getLink());
158 | } else {
159 | self::assertEquals($this->getLink($page_link, 1), $node->getLink());
160 | }
161 | }
162 |
163 | /**
164 | * @dataProvider getPageLinks
165 | *
166 | * @param string|callable $page_link
167 | */
168 | public function testGetPrev($page_link)
169 | {
170 | $this->config
171 | ->expects($this->atLeastOnce())
172 | ->method('getCurrentPage')
173 | ->willReturn(5);
174 | $this->config
175 | ->expects($this->never())
176 | ->method('getFirstPageLink')
177 | ->willReturn('');
178 | $this->config
179 | ->expects($this->atLeastOnce())
180 | ->method('getPageLink')
181 | ->willReturn($page_link);
182 |
183 | $node = $this->view->getPrev();
184 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Entity\Node', $node);
185 | self::assertEquals(4, $node->getPage());
186 | self::assertEquals($this->getLink($page_link, 4), $node->getLink());
187 | }
188 |
189 | /**
190 | * @dataProvider getFirstPageLinks
191 | *
192 | * @param string|callable $page_link
193 | * @param string $first_page_link
194 | */
195 | public function testGetCurrent($page_link, $first_page_link)
196 | {
197 | $this->config
198 | ->expects($this->atLeastOnce())
199 | ->method('getCurrentPage')
200 | ->willReturn(1);
201 | $this->config
202 | ->expects($first_page_link ? $this->atLeastOnce() : $this->once())
203 | ->method('getFirstPageLink')
204 | ->willReturn($first_page_link);
205 | $this->config
206 | ->expects($first_page_link ? $this->never() : $this->atLeastOnce())
207 | ->method('getPageLink')
208 | ->willReturn($page_link);
209 |
210 | $node = $this->view->getCurrent();
211 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Entity\Node', $node);
212 | self::assertEquals(1, $node->getPage());
213 | if ($first_page_link) {
214 | self::assertEquals($first_page_link, $node->getLink());
215 | } else {
216 | self::assertEquals($this->getLink($page_link, 1), $node->getLink());
217 | }
218 | }
219 |
220 | /**
221 | * @dataProvider getPageLinks
222 | *
223 | * @param string|callable $page_link
224 | */
225 | public function testGetNext($page_link)
226 | {
227 | $this->config
228 | ->expects($this->atLeastOnce())
229 | ->method('getCurrentPage')
230 | ->willReturn(5);
231 | $this->config
232 | ->expects($this->atLeastOnce())
233 | ->method('getTotalPages')
234 | ->willReturn(10);
235 | $this->config
236 | ->expects($this->never())
237 | ->method('getFirstPageLink')
238 | ->willReturn('');
239 | $this->config
240 | ->expects($this->atLeastOnce())
241 | ->method('getPageLink')
242 | ->willReturn($page_link);
243 |
244 | $node = $this->view->getNext();
245 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Entity\Node', $node);
246 | self::assertEquals(6, $node->getPage());
247 | self::assertEquals($this->getLink($page_link, 6), $node->getLink());
248 | }
249 |
250 | /**
251 | * @dataProvider getPageLinks
252 | *
253 | * @param string|callable $page_link
254 | */
255 | public function testGetLast($page_link)
256 | {
257 | $this->config
258 | ->expects($this->atLeastOnce())
259 | ->method('getCurrentPage')
260 | ->willReturn(5);
261 | $this->config
262 | ->expects($this->atLeastOnce())
263 | ->method('getTotalPages')
264 | ->willReturn(10);
265 | $this->config
266 | ->expects($this->never())
267 | ->method('getFirstPageLink')
268 | ->willReturn('');
269 | $this->config
270 | ->expects($this->atLeastOnce())
271 | ->method('getPageLink')
272 | ->willReturn($page_link);
273 |
274 | $node = $this->view->getLast();
275 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Entity\Node', $node);
276 | self::assertEquals(10, $node->getPage());
277 | self::assertEquals($this->getLink($page_link, 10), $node->getLink());
278 | }
279 |
280 | /**
281 | * @return array
282 | */
283 | public function getNodes()
284 | {
285 | return [
286 | [
287 | 2,
288 | '/?page=%s',
289 | null,
290 | new ArrayCollection([
291 | new Node(1, '/?page=1', true),
292 | new Node(2, '/?page=2'),
293 | ]),
294 | ],
295 | [
296 | 2,
297 | '/?page=%s',
298 | null,
299 | new ArrayCollection([
300 | new Node(1, '/?page=1'),
301 | new Node(2, '/?page=2', true),
302 | ]),
303 | ],
304 | [
305 | 10,
306 | '/?page=%s',
307 | null,
308 | new ArrayCollection([
309 | new Node(1, '/?page=1', true),
310 | new Node(2, '/?page=2'),
311 | new Node(3, '/?page=3'),
312 | new Node(4, '/?page=4'),
313 | new Node(5, '/?page=5'),
314 | ]),
315 | ],
316 | [
317 | 10,
318 | '/?page=%s',
319 | null,
320 | new ArrayCollection([
321 | new Node(6, '/?page=6'),
322 | new Node(7, '/?page=7'),
323 | new Node(8, '/?page=8'),
324 | new Node(9, '/?page=9'),
325 | new Node(10, '/?page=10', true),
326 | ]),
327 | ],
328 | [
329 | 10,
330 | '/?page=%s',
331 | null,
332 | new ArrayCollection([
333 | new Node(3, '/?page=3'),
334 | new Node(4, '/?page=4'),
335 | new Node(5, '/?page=5', true),
336 | new Node(6, '/?page=6'),
337 | new Node(7, '/?page=7'),
338 | ]),
339 | ],
340 | [
341 | 10,
342 | function ($number) {
343 | return sprintf('/?page=%s', $number);
344 | },
345 | '/',
346 | new ArrayCollection([
347 | new Node(4, '/?page=4'),
348 | new Node(5, '/?page=5', true),
349 | new Node(6, '/?page=6'),
350 | new Node(7, '/?page=7'),
351 | ]),
352 | ],
353 | ];
354 | }
355 |
356 | /**
357 | * @dataProvider getNodes
358 | *
359 | * @param int $total_pages
360 | * @param string|\Closure $page_link
361 | * @param string $first_page_link
362 | * @param ArrayCollection $list
363 | */
364 | public function testGetIterator($total_pages, $page_link, $first_page_link, $list)
365 | {
366 | $current_page = 1;
367 | foreach ($list as $node) {
368 | /** @var $node Node */
369 | if ($node->isCurrent()) {
370 | $current_page = $node->getPage();
371 | }
372 | }
373 |
374 | $left_offset = $current_page - $list->first()->getPage();
375 | $right_offset = $list->last()->getPage() - $current_page;
376 |
377 | if ($list->first()->getPage() === 1) {
378 | $this->config
379 | ->expects($this->once())
380 | ->method('getFirstPageLink')
381 | ->willReturn($first_page_link);
382 | } else {
383 | $this->config
384 | ->expects($this->never())
385 | ->method('getFirstPageLink');
386 | }
387 |
388 | $this->config
389 | ->expects($this->once())
390 | ->method('getTotalPages')
391 | ->willReturn($total_pages);
392 | $this->config
393 | ->expects($this->atLeastOnce())
394 | ->method('getCurrentPage')
395 | ->willReturn($current_page);
396 | $this->config
397 | ->expects($this->atLeastOnce())
398 | ->method('getPageLink')
399 | ->willReturn($page_link);
400 |
401 | $this->range
402 | ->expects($this->once())
403 | ->method('getLeftOffset')
404 | ->willReturn($left_offset);
405 | $this->range
406 | ->expects($this->once())
407 | ->method('getRightOffset')
408 | ->willReturn($right_offset);
409 |
410 | self::assertEquals($list, $this->view->getIterator());
411 | }
412 |
413 | public function testGetIteratorEmpty()
414 | {
415 | $this->config
416 | ->expects($this->once())
417 | ->method('getTotalPages')
418 | ->willReturn(1);
419 | $this->config
420 | ->expects($this->never())
421 | ->method('getCurrentPage');
422 | $this->config
423 | ->expects($this->never())
424 | ->method('getPageLink');
425 | $this->config
426 | ->expects($this->never())
427 | ->method('getFirstPageLink');
428 |
429 | $this->range
430 | ->expects($this->never())
431 | ->method('getLeftOffset');
432 | $this->range
433 | ->expects($this->never())
434 | ->method('getRightOffset');
435 |
436 | self::assertEquals(new ArrayCollection(), $this->view->getIterator());
437 | }
438 | }
439 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests;
12 |
13 | use PHPUnit\Framework\MockObject\MockObject;
14 | use PHPUnit\Framework\TestCase as BaseTestCase;
15 |
16 | class TestCase extends BaseTestCase
17 | {
18 | /**
19 | * @param string $class_name
20 | *
21 | * @return \PHPUnit_Framework_MockObject_MockObject|MockObject
22 | */
23 | protected function getMockNoConstructor($class_name)
24 | {
25 | return $this
26 | ->getMockBuilder($class_name)
27 | ->disableOriginalConstructor()
28 | ->disableOriginalClone()
29 | ->getMock();
30 | }
31 |
32 | /**
33 | * @param string $class_name
34 | *
35 | * @return \PHPUnit_Framework_MockObject_MockObject|MockObject
36 | */
37 | protected function getMockAbstract($class_name, array $methods)
38 | {
39 | return $this
40 | ->getMockBuilder($class_name)
41 | ->disableOriginalConstructor()
42 | ->setMethods($methods)
43 | ->getMockForAbstractClass();
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/tests/Twig/Extension/PaginationExtensionTest.php:
--------------------------------------------------------------------------------
1 |
7 | * @copyright Copyright (c) 2011, Peter Gribanov
8 | * @license http://opensource.org/licenses/MIT
9 | */
10 |
11 | namespace GpsLab\Bundle\PaginationBundle\Tests\Twig\Extension;
12 |
13 | use GpsLab\Bundle\PaginationBundle\Service\Configuration;
14 | use GpsLab\Bundle\PaginationBundle\Tests\TestCase;
15 | use GpsLab\Bundle\PaginationBundle\Twig\Extension\PaginationExtension;
16 |
17 | class PaginationExtensionTest extends TestCase
18 | {
19 | /**
20 | * @var PaginationExtension
21 | */
22 | private $extension;
23 |
24 | /**
25 | * @var string
26 | */
27 | private $template = 'foo';
28 |
29 | protected function setUp()
30 | {
31 | $this->extension = new PaginationExtension($this->template);
32 | }
33 |
34 | public function testGetFunctions()
35 | {
36 | $functions = $this->extension->getFunctions();
37 |
38 | self::assertInternalType('array', $functions);
39 | self::assertCount(1, $functions);
40 | self::assertInstanceOf('Twig\TwigFunction', $functions[0]);
41 | }
42 |
43 | public function testRender()
44 | {
45 | $expected = 'bar';
46 | $view = 'baz';
47 | /* @var $env \PHPUnit_Framework_MockObject_MockObject|\Twig\Environment */
48 | $env = $this->getMockNoConstructor('Twig\Environment');
49 | $env
50 | ->expects($this->once())
51 | ->method('render')
52 | ->with($this->template, ['pagination' => $view])
53 | ->willReturn($expected);
54 |
55 | /* @var $configuration \PHPUnit_Framework_MockObject_MockObject|Configuration */
56 | $configuration = $this->getMockNoConstructor('GpsLab\Bundle\PaginationBundle\Service\Configuration');
57 | $configuration
58 | ->expects($this->once())
59 | ->method('getView')
60 | ->willReturn($view);
61 |
62 | self::assertEquals($expected, $this->extension->renderPagination(
63 | $env,
64 | $configuration
65 | ));
66 | }
67 |
68 | public function testRenderChangeTemplate()
69 | {
70 | $expected = 'bar';
71 | $view = 'baz';
72 | $template = 'my_template';
73 | /* @var $env \PHPUnit_Framework_MockObject_MockObject|\Twig\Environment */
74 | $env = $this->getMockNoConstructor('Twig\Environment');
75 | $env
76 | ->expects($this->once())
77 | ->method('render')
78 | ->with($template, ['pagination' => $view, 'my_params' => 12345])
79 | ->willReturn($expected);
80 |
81 | /* @var $configuration \PHPUnit_Framework_MockObject_MockObject|Configuration */
82 | $configuration = $this->getMockNoConstructor('GpsLab\Bundle\PaginationBundle\Service\Configuration');
83 | $configuration
84 | ->expects($this->once())
85 | ->method('getView')
86 | ->willReturn($view);
87 |
88 | self::assertEquals($expected, $this->extension->renderPagination(
89 | $env,
90 | $configuration,
91 | $template,
92 | ['my_params' => 12345]
93 | ));
94 | }
95 |
96 | public function testRenderNoOverrideTemplateParams()
97 | {
98 | $expected = 'bar';
99 | $view = 'baz';
100 | /* @var $env \PHPUnit_Framework_MockObject_MockObject|\Twig\Environment */
101 | $env = $this->getMockNoConstructor('Twig\Environment');
102 | $env
103 | ->expects($this->once())
104 | ->method('render')
105 | ->with($this->template, ['pagination' => $view])
106 | ->willReturn($expected);
107 |
108 | /* @var $configuration \PHPUnit_Framework_MockObject_MockObject|Configuration */
109 | $configuration = $this->getMockNoConstructor('GpsLab\Bundle\PaginationBundle\Service\Configuration');
110 | $configuration
111 | ->expects($this->once())
112 | ->method('getView')
113 | ->willReturn($view);
114 |
115 | self::assertEquals($expected, $this->extension->renderPagination(
116 | $env,
117 | $configuration,
118 | null,
119 | ['pagination' => 12345]
120 | ));
121 | }
122 |
123 | public function testRenderWithCustomMaxNavigate()
124 | {
125 | $old_max_navigate = 5;
126 | $new_max_navigate = 10;
127 | $expected = 'bar';
128 |
129 | $configuration = new Configuration();
130 | $configuration->setTotalPages(100);
131 | $configuration->setMaxNavigate($old_max_navigate);
132 |
133 | /* @var $env \PHPUnit_Framework_MockObject_MockObject|\Twig\Environment */
134 | $env = $this->getMockNoConstructor('Twig\Environment');
135 | $env
136 | ->expects($this->once())
137 | ->method('render')
138 | ->willReturnCallback(function ($template, array $context) use ($expected, $configuration, $new_max_navigate) {
139 | self::assertSame($this->template, $template);
140 | self::assertArrayHasKey('pagination', $context);
141 | self::assertSame(['pagination'], array_keys($context), 'Context has only "pagination" key.');
142 | $view = $context['pagination'];
143 | self::assertInstanceOf('GpsLab\Bundle\PaginationBundle\Service\View', $view);
144 | self::assertNotEquals($configuration->getView(), $view);
145 | self::assertCount($new_max_navigate, iterator_to_array($view));
146 |
147 | return $expected;
148 | });
149 |
150 | self::assertSame($expected, $this->extension->renderPagination(
151 | $env,
152 | $configuration,
153 | null,
154 | [],
155 | $new_max_navigate
156 | ));
157 | self::assertSame($old_max_navigate, $configuration->getMaxNavigate(), 'The max navigate is not changed in original object.');
158 | }
159 |
160 | public function testGetName()
161 | {
162 | self::assertEquals('gpslab_pagination_extension', $this->extension->getName());
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 |