1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace KI\CoreBundle\Helper; |
4
|
|
|
|
5
|
|
|
use Doctrine\ORM\EntityManager; |
6
|
|
|
use Doctrine\ORM\EntityRepository; |
7
|
|
|
use Symfony\Component\HttpFoundation\JsonResponse; |
8
|
|
|
use Symfony\Component\HttpFoundation\RequestStack; |
9
|
|
|
use Symfony\Component\HttpFoundation\Response; |
10
|
|
|
|
11
|
|
|
class PaginateHelper |
12
|
|
|
{ |
13
|
|
|
protected $manager; |
14
|
|
|
protected $request; |
15
|
|
|
|
16
|
|
|
public function __construct(EntityManager $manager) |
|
|
|
|
17
|
|
|
{ |
18
|
|
|
$this->manager = $manager; |
19
|
|
|
} |
20
|
|
|
|
21
|
|
|
public function setRequest(RequestStack $requestStack) |
22
|
|
|
{ |
23
|
|
|
$this->request = $requestStack->getCurrentRequest(); |
24
|
|
|
} |
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* En fonction de la requête, récupère les données utiles à la pagination |
28
|
|
|
* @param EntityRepository $repository Le repository sur lequel effectuer les comptes |
29
|
|
|
* @return array Les données de pagination (nombre de pages, etc.) |
30
|
|
|
*/ |
31
|
|
|
public function paginateData(EntityRepository $repository, array $findBy = []) |
32
|
|
|
{ |
33
|
|
|
$queryBuilder = $repository->createQueryBuilder('o'); |
34
|
|
|
$request = $this->request->query; |
35
|
|
|
|
36
|
|
|
// On s'assure de bien recevoir des arrays |
37
|
|
|
foreach ($findBy as $key => $value) { |
38
|
|
|
$findBy[$key] = array($value); |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
// On récupère les paramètres de la requête |
42
|
|
|
$page = $request->has('page') ? $request->get('page') : 1; |
43
|
|
|
$limit = $request->has('limit') ? $request->get('limit') : 100; |
44
|
|
|
$sort = $request->has('sort') ? $request->get('sort') : null; |
45
|
|
|
|
46
|
|
|
if ($sort === null) { |
47
|
|
|
$sortBy = ['id' => 'DESC']; |
48
|
|
|
} else { |
49
|
|
|
$sortBy = []; |
50
|
|
|
|
51
|
|
|
foreach (explode(',', $sort) as $value) { |
52
|
|
|
$order = preg_match('/^\-.*/isU', $value) ? 'DESC' : 'ASC'; |
53
|
|
|
$field = preg_replace('/^\-/isU', '', $value); |
54
|
|
|
$sortBy[$field] = $order; |
55
|
|
|
} |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
foreach ($request->all() as $key => $values) { |
59
|
|
|
if ($key != 'page' && $key != 'limit' && $key != 'sort') { |
60
|
|
|
$findBy[$key] = explode(',', $values); |
61
|
|
|
} |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
// On compte le nombre total d'entrées dans la BDD |
65
|
|
|
$queryBuilder->select('count(o.id)'); |
66
|
|
|
foreach ($findBy as $key => $values){ |
67
|
|
|
$andCount = 0; |
68
|
|
|
$and = ''; |
69
|
|
|
foreach($values as $value){ |
70
|
|
|
if($andCount > 0){ |
71
|
|
|
$and .= ' OR '; |
72
|
|
|
} |
73
|
|
|
$and .= 'o.' . $key . ' = :' . $key . $andCount; |
74
|
|
|
$queryBuilder->setParameter($key . $andCount, $value); |
75
|
|
|
|
76
|
|
|
$andCount++; |
77
|
|
|
} |
78
|
|
|
$queryBuilder->andWhere($and); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
foreach($sortBy as $field => $order){ |
82
|
|
|
$queryBuilder->addOrderBy('o.' . $field, $order); |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
$count = $queryBuilder->getQuery()->getSingleScalarResult(); |
86
|
|
|
|
87
|
|
|
// On vérifie que l'utilisateur ne fasse pas de connerie avec les variables |
88
|
|
|
$totalPages = ceil($count/$limit); |
89
|
|
|
$limit = min($limit, 10000); |
90
|
|
|
$limit = max($limit, 1); |
91
|
|
|
$page = min($page, $totalPages); |
92
|
|
|
$page = max($page, 1); |
93
|
|
|
|
94
|
|
|
$results = $queryBuilder->select('o') |
95
|
|
|
->setMaxResults($limit) |
96
|
|
|
->setFirstResult(($page - 1)*$limit) |
97
|
|
|
->getQuery() |
98
|
|
|
->getResult(); |
99
|
|
|
|
100
|
|
|
return [ |
101
|
|
|
'findBy' => $findBy, |
102
|
|
|
'sortBy' => $sortBy, |
103
|
|
|
'limit' => $limit, |
104
|
|
|
'offset' => ($page - 1)*$limit, |
105
|
|
|
'page' => $page, |
106
|
|
|
'totalPages' => $totalPages, |
107
|
|
|
'count' => $count, |
108
|
|
|
'results' => $results, |
109
|
|
|
]; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
/** |
113
|
|
|
* Génère les headers de pagination et renvoie la réponse |
114
|
|
|
* @param array $results Les résultats à paginer |
115
|
|
|
* @param integer $limit Le nombre d'objets par page |
116
|
|
|
* @param integer $page Le numéro de la page en cours |
117
|
|
|
* @param integer $totalPages Le nombre total de pages |
118
|
|
|
* @param integer $count Le nombre total d'objets |
119
|
|
|
* @return Response |
120
|
|
|
*/ |
121
|
|
|
public function paginateView($results, $limit, $page, $totalPages, $count) |
122
|
|
|
{ |
123
|
|
|
// On prend l'url de la requête |
124
|
|
|
$baseUrl = '<'.str_replace($this->request->getBaseUrl(), '', $this->request->getRequestUri()); |
125
|
|
|
|
126
|
|
|
// On enlève tous les paramètres GET de type "page" et "limit" précédents s'il y en avait |
127
|
|
|
$baseUrl = preg_replace('/[\?&](page|limit)=\d+/', '', $baseUrl); |
128
|
|
|
$baseUrl .= !preg_match('/\?/', $baseUrl) ? '?' : '&'; |
129
|
|
|
|
130
|
|
|
// On va générer les notres pour les links |
131
|
|
|
$baseUrl .= 'page='; |
132
|
|
|
$links = []; |
133
|
|
|
|
134
|
|
|
// First |
135
|
|
|
$links[] = $baseUrl.'1'.'&limit='.$limit.'>;rel=first'; |
136
|
|
|
|
137
|
|
|
// Previous |
138
|
|
View Code Duplication |
if ($page > 1) { |
139
|
|
|
$links[] = $baseUrl.($page - 1).'&limit='.$limit.'>;rel=previous'; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
// Self |
143
|
|
|
$links[] = $baseUrl.$page.'&limit='.$limit.'>;rel=self'; |
144
|
|
|
|
145
|
|
|
// Next |
146
|
|
View Code Duplication |
if ($page < $totalPages) { |
147
|
|
|
$links[] = $baseUrl.($page + 1).'&limit='.$limit.'>;rel=next'; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
// Last |
151
|
|
|
$links[] = $baseUrl.$totalPages.'&limit='.$limit.'>;rel=last'; |
152
|
|
|
|
153
|
|
|
return [ |
154
|
|
|
$results, |
155
|
|
|
$links, |
156
|
|
|
$count |
157
|
|
|
]; |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.