Skip to content

Commit 309bd35

Browse files
committed
feature #1006 Added a Paginator object based on Doctrine's paginator (javiereguiluz)
This PR was squashed before being merged into the master branch (closes #1006). Discussion ---------- Added a Paginator object based on Doctrine's paginator The current code uses an array to provide the pagination information. We did that as part of the quick proof-of-concept replacement of Pagerfanta library. This PR proposes to replace that array by a proper object. The new design was based on the one proposed by EasyAdmin to also replace Pagerfanta (EasyCorp/EasyAdminBundle#2777). Commits ------- 7a9e13e Added a Paginator object based on Doctrine's paginator
2 parents 6bdc4ce + 7a9e13e commit 309bd35

File tree

3 files changed

+115
-33
lines changed

3 files changed

+115
-33
lines changed

src/Pagination/Paginator.php

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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 App\Pagination;
13+
14+
use Doctrine\ORM\QueryBuilder as DoctrineQueryBuilder;
15+
use Doctrine\ORM\Tools\Pagination\CountWalker;
16+
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
17+
18+
/**
19+
* @author Javier Eguiluz <[email protected]>
20+
*/
21+
class Paginator
22+
{
23+
private const PAGE_SIZE = 10;
24+
private $queryBuilder;
25+
private $currentPage;
26+
private $pageSize;
27+
private $results;
28+
private $numResults;
29+
30+
public function __construct(DoctrineQueryBuilder $queryBuilder, int $pageSize = self::PAGE_SIZE)
31+
{
32+
$this->queryBuilder = $queryBuilder;
33+
$this->pageSize = $pageSize;
34+
}
35+
36+
public function paginate(int $page = 1): self
37+
{
38+
$this->currentPage = max(1, $page);
39+
$firstResult = ($this->currentPage - 1) * $this->pageSize;
40+
41+
$query = $this->queryBuilder
42+
->setFirstResult($firstResult)
43+
->setMaxResults($this->pageSize)
44+
->getQuery();
45+
46+
if (0 === \count($this->queryBuilder->getDQLPart('join'))) {
47+
$query->setHint(CountWalker::HINT_DISTINCT, false);
48+
}
49+
50+
$paginator = new DoctrinePaginator($query, true);
51+
52+
$useOutputWalkers = \count($this->queryBuilder->getDQLPart('having') ?: []) > 0;
53+
$paginator->setUseOutputWalkers($useOutputWalkers);
54+
55+
$this->results = $paginator->getIterator();
56+
$this->numResults = $paginator->count();
57+
58+
return $this;
59+
}
60+
61+
public function getCurrentPage(): int
62+
{
63+
return $this->currentPage;
64+
}
65+
66+
public function getLastPage(): int
67+
{
68+
return (int) ceil($this->numResults / $this->pageSize);
69+
}
70+
71+
public function getPageSize(): int
72+
{
73+
return $this->pageSize;
74+
}
75+
76+
public function hasPreviousPage(): bool
77+
{
78+
return $this->currentPage > 1;
79+
}
80+
81+
public function getPreviousPage(): int
82+
{
83+
return max(1, $this->currentPage - 1);
84+
}
85+
86+
public function hasNextPage(): bool
87+
{
88+
return $this->currentPage < $this->getLastPage();
89+
}
90+
91+
public function getNextPage(): int
92+
{
93+
return min($this->getLastPage(), $this->currentPage + 1);
94+
}
95+
96+
public function hasToPaginate(): bool
97+
{
98+
return $this->numResults > $this->pageSize;
99+
}
100+
101+
public function getNumResults(): int
102+
{
103+
return $this->numResults;
104+
}
105+
106+
public function getResults(): \Traversable
107+
{
108+
return $this->results;
109+
}
110+
}

src/Repository/PostRepository.php

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,9 @@
1313

1414
use App\Entity\Post;
1515
use App\Entity\Tag;
16+
use App\Pagination\Paginator;
1617
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
1718
use Doctrine\Common\Persistence\ManagerRegistry;
18-
use Doctrine\ORM\QueryBuilder;
19-
use Doctrine\ORM\Tools\Pagination\Paginator;
2019

2120
/**
2221
* This custom Doctrine repository contains some methods which are useful when
@@ -35,7 +34,7 @@ public function __construct(ManagerRegistry $registry)
3534
parent::__construct($registry, Post::class);
3635
}
3736

38-
public function findLatest(int $page = 1, Tag $tag = null): array
37+
public function findLatest(int $page = 1, Tag $tag = null): Paginator
3938
{
4039
$qb = $this->createQueryBuilder('p')
4140
->addSelect('a', 't')
@@ -51,7 +50,7 @@ public function findLatest(int $page = 1, Tag $tag = null): array
5150
->setParameter('tag', $tag);
5251
}
5352

54-
return $this->createPaginator($qb, $page);
53+
return (new Paginator($qb))->paginate($page);
5554
}
5655

5756
/**
@@ -101,31 +100,4 @@ private function extractSearchTerms(string $searchQuery): array
101100
return 2 <= mb_strlen($term);
102101
});
103102
}
104-
105-
private function createPaginator(QueryBuilder $queryBuilder, int $currentPage, int $pageSize = Post::NUM_ITEMS)
106-
{
107-
$currentPage = $currentPage < 1 ? 1 : $currentPage;
108-
$firstResult = ($currentPage - 1) * $pageSize;
109-
110-
$query = $queryBuilder
111-
->setFirstResult($firstResult)
112-
->setMaxResults($pageSize)
113-
->getQuery();
114-
115-
$paginator = new Paginator($query);
116-
$numResults = $paginator->count();
117-
$hasPreviousPage = $currentPage > 1;
118-
$hasNextPage = ($currentPage * $pageSize) < $numResults;
119-
120-
return [
121-
'results' => $paginator->getIterator(),
122-
'currentPage' => $currentPage,
123-
'hasPreviousPage' => $hasPreviousPage,
124-
'hasNextPage' => $hasNextPage,
125-
'previousPage' => $hasPreviousPage ? $currentPage - 1 : null,
126-
'nextPage' => $hasNextPage ? $currentPage + 1 : null,
127-
'numPages' => (int) ceil($numResults / $pageSize),
128-
'haveToPaginate' => $numResults > $pageSize,
129-
];
130-
}
131103
}

templates/blog/index.html.twig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<div class="well">{{ 'post.no_posts_found'|trans }}</div>
2525
{% endfor %}
2626

27-
{% if paginator.haveToPaginate %}
27+
{% if paginator.hasToPaginate %}
2828
<div class="navigation text-center">
2929
<ul class="pagination">
3030
{% if paginator.hasPreviousPage %}
@@ -33,7 +33,7 @@
3333
<li class="prev disabled"><span><i class="fa fw fa-arrow-left"></i> Previous</span></li>
3434
{% endif %}
3535

36-
{% for i in 1..paginator.numPages %}
36+
{% for i in 1..paginator.lastPage %}
3737
{% if i == paginator.currentPage %}
3838
<li class="active"><span>{{ i }} <span class="sr-only">(current)</span></span></li>
3939
{% else %}

0 commit comments

Comments
 (0)