1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Paymaxi\Component\Query; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\Collections\Criteria; |
8
|
|
|
use Doctrine\ORM\EntityRepository; |
9
|
|
|
use Paymaxi\Component\Query\Filter\FilterInterface; |
10
|
|
|
use Paymaxi\Component\Query\Handler\AbstractHandler; |
11
|
|
|
use Paymaxi\Component\Query\Handler\CriteriaHandler; |
12
|
|
|
use Paymaxi\Component\Query\Handler\FilterHandlerInterface; |
13
|
|
|
use Paymaxi\Component\Query\Handler\HandlerInterface; |
14
|
|
|
use Paymaxi\Component\Query\Handler\QueryBuilderHandler; |
15
|
|
|
use Paymaxi\Component\Query\Handler\SortHandlerInterface; |
16
|
|
|
use Paymaxi\Component\Query\Sort\SortInterface; |
17
|
|
|
use Sylius\Component\Registry\ServiceRegistry; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Class CriteriaQueryBuilder |
21
|
|
|
* |
22
|
|
|
*/ |
23
|
|
|
class CriteriaQueryBuilder implements CriteriaQueryBuilderInterface |
24
|
|
|
{ |
25
|
|
|
/** @var Criteria */ |
26
|
|
|
protected $criteria; |
27
|
|
|
|
28
|
|
|
/** @var \Doctrine\ORM\QueryBuilder */ |
29
|
|
|
protected $qb; |
30
|
|
|
|
31
|
|
|
/** @var array */ |
32
|
|
|
private $filterParams; |
33
|
|
|
|
34
|
|
|
/** @var array */ |
35
|
|
|
private $sortingFields; |
36
|
|
|
|
37
|
|
|
/** @var array */ |
38
|
|
|
private $defaultOrder; |
39
|
|
|
|
40
|
|
|
/** @var ServiceRegistry|HandlerInterface[]|AbstractHandler[] */ |
41
|
|
|
private $handlers; |
42
|
|
|
|
43
|
|
|
/** @var bool */ |
44
|
|
|
private $applied = false; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* @param EntityRepository $repository |
48
|
|
|
* @param array $filterParams |
49
|
|
|
* @param array $sortingFields |
50
|
|
|
* |
51
|
|
|
* @internal param ApiManagerInterface $manager |
52
|
|
|
*/ |
53
|
4 |
|
public function __construct(EntityRepository $repository, array $filterParams = [], array $sortingFields = []) |
54
|
|
|
{ |
55
|
4 |
|
$this->qb = $repository->createQueryBuilder('e'); |
56
|
4 |
|
$this->criteria = new Criteria(); |
57
|
4 |
|
$this->handlers = new ServiceRegistry(HandlerInterface::class); |
58
|
|
|
|
59
|
4 |
|
$this->initDefaultHandlers(); |
60
|
4 |
|
$this->setFilterParams($filterParams); |
61
|
4 |
|
$this->setSortingFields($sortingFields); |
62
|
4 |
|
} |
63
|
|
|
|
64
|
4 |
|
protected function initDefaultHandlers(): void |
65
|
|
|
{ |
66
|
4 |
|
$this->handlers->register(CriteriaHandler::class, new CriteriaHandler($this->criteria)); |
67
|
4 |
|
$this->handlers->register(QueryBuilderHandler::class, new QueryBuilderHandler($this->qb)); |
68
|
4 |
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @param FilterInterface $filter |
72
|
|
|
* |
73
|
|
|
* @return CriteriaQueryBuilderInterface |
74
|
|
|
*/ |
75
|
4 |
View Code Duplication |
public function addFilter(FilterInterface $filter): CriteriaQueryBuilderInterface |
|
|
|
|
76
|
|
|
{ |
77
|
4 |
|
$supports = false; |
78
|
|
|
|
79
|
|
|
/** @var AbstractHandler $handler */ |
80
|
4 |
|
foreach ($this->handlers->all() as $handler) { |
81
|
4 |
|
if ($handler instanceof FilterHandlerInterface && $handler->supports($filter)) { |
82
|
4 |
|
$supports = true; |
83
|
4 |
|
$handler->addFilter($filter); |
84
|
|
|
} |
85
|
|
|
} |
86
|
|
|
|
87
|
4 |
|
if (!$supports) { |
88
|
|
|
throw new \RuntimeException('No available handler for this filter.'); |
89
|
|
|
} |
90
|
|
|
|
91
|
4 |
|
$this->resetApply(); |
92
|
|
|
|
93
|
4 |
|
return $this; |
94
|
|
|
} |
95
|
|
|
|
96
|
4 |
|
protected function resetApply():void |
97
|
|
|
{ |
98
|
4 |
|
$this->applied = false; |
99
|
4 |
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @param SortInterface $sort |
103
|
|
|
* |
104
|
|
|
* @return CriteriaQueryBuilderInterface |
105
|
|
|
*/ |
106
|
|
View Code Duplication |
public function addSorting(SortInterface $sort): CriteriaQueryBuilderInterface |
|
|
|
|
107
|
|
|
{ |
108
|
|
|
$supports = false; |
109
|
|
|
|
110
|
|
|
/** @var AbstractHandler $handler */ |
111
|
|
|
foreach ($this->handlers->all() as $handler) { |
112
|
|
|
if ($handler instanceof SortHandlerInterface && $handler->supports($sort)) { |
113
|
|
|
$supports = true; |
114
|
|
|
$handler->addSorting($sort); |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
if (!$supports) { |
119
|
|
|
throw new \RuntimeException('No available handler for this sorting.'); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
$this->resetApply(); |
123
|
|
|
|
124
|
|
|
return $this; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* @return \Doctrine\ORM\QueryBuilder |
129
|
|
|
* @throws \Doctrine\ORM\Query\QueryException |
130
|
|
|
*/ |
131
|
3 |
|
public function getQb(): \Doctrine\ORM\QueryBuilder |
132
|
|
|
{ |
133
|
3 |
|
$clone = clone $this; |
134
|
3 |
|
$clone->apply(); |
135
|
|
|
|
136
|
3 |
|
return $clone->qb->addCriteria($clone->getCriteria()); |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* It caused changes in qb and criteria |
141
|
|
|
*/ |
142
|
4 |
|
protected function apply(): void |
143
|
|
|
{ |
144
|
4 |
|
if ($this->applied) { |
145
|
3 |
|
return; |
146
|
|
|
} |
147
|
|
|
|
148
|
4 |
|
if (0 === \count($this->sortingFields)) { |
149
|
4 |
|
$this->criteria->orderBy($this->getDefaultOrder()); |
150
|
|
|
} |
151
|
|
|
|
152
|
4 |
|
$this->applySorting(); |
153
|
4 |
|
$this->applyFilters(); |
154
|
|
|
|
155
|
4 |
|
$this->applied = true; |
156
|
4 |
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* @return Criteria |
160
|
|
|
*/ |
161
|
4 |
|
public function getCriteria(): Criteria |
162
|
|
|
{ |
163
|
4 |
|
$this->apply(); |
164
|
|
|
|
165
|
4 |
|
return $this->criteria; |
166
|
|
|
} |
167
|
|
|
|
168
|
4 |
|
private function applyFilters() |
169
|
|
|
{ |
170
|
4 |
|
foreach ($this->filterParams as $field => $value) { |
171
|
3 |
|
foreach ($this->handlers->all() as $handler) { |
172
|
3 |
|
if ($handler instanceof FilterHandlerInterface) { |
173
|
3 |
|
$handler->filter($field, $value); |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
} |
177
|
4 |
|
} |
178
|
|
|
|
179
|
4 |
|
private function applySorting() |
180
|
|
|
{ |
181
|
4 |
|
foreach ($this->sortingFields as $field => $order) { |
182
|
|
|
foreach ($this->handlers->all() as $handler) { |
183
|
|
|
if ($handler instanceof SortHandlerInterface) { |
184
|
|
|
$handler->sort($field, $order); |
185
|
|
|
} |
186
|
|
|
} |
187
|
|
|
} |
188
|
4 |
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* @return array |
192
|
|
|
*/ |
193
|
4 |
|
public function getDefaultOrder(): array |
194
|
|
|
{ |
195
|
4 |
|
return !empty($this->defaultOrder) ? $this->defaultOrder : ['created' => 'DESC']; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* @param array $defaultOrder |
200
|
|
|
*/ |
201
|
4 |
|
public function setDefaultOrder(array $defaultOrder): void |
202
|
|
|
{ |
203
|
4 |
|
$this->defaultOrder = $defaultOrder; |
204
|
4 |
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* @param array $filterParams |
208
|
|
|
*/ |
209
|
4 |
|
public function setFilterParams(array $filterParams) |
210
|
|
|
{ |
211
|
4 |
|
$this->resetApply(); |
212
|
|
|
|
213
|
4 |
|
$this->filterParams = $filterParams; |
214
|
4 |
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @param array $sortingFields |
218
|
|
|
*/ |
219
|
4 |
|
public function setSortingFields(array $sortingFields) |
220
|
|
|
{ |
221
|
4 |
|
$this->resetApply(); |
222
|
|
|
|
223
|
4 |
|
$this->sortingFields = $sortingFields; |
224
|
4 |
|
} |
225
|
|
|
|
226
|
|
|
/** |
227
|
|
|
* @return ServiceRegistry |
228
|
|
|
*/ |
229
|
|
|
public function getHandlers():ServiceRegistry |
230
|
|
|
{ |
231
|
|
|
return $this->handlers; |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
/** |
235
|
|
|
* @param string $identifier |
236
|
|
|
* @param HandlerInterface $handler |
237
|
|
|
*/ |
238
|
|
|
public function addHandler(string $identifier, HandlerInterface $handler):void |
239
|
|
|
{ |
240
|
|
|
$this->handlers->register($identifier, $handler); |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.