1
|
|
|
<?php |
2
|
|
|
/* |
3
|
|
|
* This file is part of the reva2/jsonapi. |
4
|
|
|
* |
5
|
|
|
* (c) Sergey Revenko <[email protected]> |
6
|
|
|
* |
7
|
|
|
* For the full copyright and license information, please view the LICENSE |
8
|
|
|
* file that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
|
12
|
|
|
namespace Reva2\JsonApi\Http\Query; |
13
|
|
|
|
14
|
|
|
use Doctrine\Common\Proxy\Exception\InvalidArgumentException; |
15
|
|
|
use Neomerx\JsonApi\Contracts\Encoder\Parameters\SortParameterInterface; |
16
|
|
|
use Neomerx\JsonApi\Encoder\Parameters\SortParameter; |
17
|
|
|
use Symfony\Component\Validator\Context\ExecutionContextInterface; |
18
|
|
|
use Reva2\JsonApi\Annotations as API; |
19
|
|
|
use Symfony\Component\Validator\Constraints as Assert; |
20
|
|
|
|
21
|
|
|
/** |
22
|
|
|
* JSON API resources list parameters |
23
|
|
|
* |
24
|
|
|
* @package Reva2\JsonApi\Http\Query |
25
|
|
|
* @author Sergey Revenko <[email protected]> |
26
|
|
|
*/ |
27
|
|
|
class ListQueryParameters extends QueryParameters |
28
|
|
|
{ |
29
|
|
|
const INVALID_PAGE_SIZE = '400b395f-ee4f-4138-aad8-89f9a1872291'; |
30
|
|
|
const INVALID_SORTING = 'e57fef44-21e4-48c4-a30d-b92009a0c16a'; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var integer|null |
34
|
|
|
* @API\Property(type="integer", path="[page][number]") |
35
|
|
|
* @Assert\Type(type="integer") |
36
|
|
|
* @Assert\GreaterThan(value=0) |
37
|
|
|
*/ |
38
|
|
|
protected $pageNumber; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var integer|null |
42
|
|
|
* @API\Property(type="integer", path="[page][size]") |
43
|
|
|
* @Assert\Type(type="integer") |
44
|
|
|
* @Assert\GreaterThan(value=0) |
45
|
|
|
*/ |
46
|
|
|
protected $pageSize; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var SortParameterInterface[]|null |
50
|
|
|
* @API\Property(path="[sort]", parser="parseSortingParameters") |
51
|
|
|
* @Assert\Type(type="array") |
52
|
|
|
*/ |
53
|
|
|
protected $sortParameters; |
54
|
|
|
|
55
|
|
|
/** |
56
|
|
|
* @param int|null $pageNumber |
57
|
|
|
* @return $this |
58
|
|
|
*/ |
59
|
1 |
|
public function setPageNumber($pageNumber = null) |
60
|
|
|
{ |
61
|
1 |
|
$this->pageNumber = $pageNumber; |
62
|
|
|
|
63
|
1 |
|
return $this; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @param int|null $pageSize |
68
|
|
|
* @return $this |
69
|
|
|
*/ |
70
|
2 |
|
public function setPageSize($pageSize = null) |
71
|
|
|
{ |
72
|
2 |
|
$this->pageSize = $pageSize; |
73
|
|
|
|
74
|
2 |
|
return $this; |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* @inheritdoc |
79
|
|
|
*/ |
80
|
2 |
|
public function getPaginationParameters() |
81
|
|
|
{ |
82
|
|
|
return [ |
83
|
2 |
|
'number' => ($this->pageNumber) ? $this->pageNumber : 1, |
84
|
2 |
|
'size' => ($this->pageSize) ? $this->pageSize : $this->getDefaultPageSize() |
85
|
2 |
|
]; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Sets sorting parameters |
90
|
|
|
* |
91
|
|
|
* @param SortParameterInterface[]|null $sorting |
92
|
|
|
* @return $this |
93
|
|
|
*/ |
94
|
2 |
|
public function setSortParameters(array $sorting = null) |
95
|
|
|
{ |
96
|
2 |
|
$this->sortParameters = $sorting; |
97
|
|
|
|
98
|
2 |
|
return $this; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @inheritdoc |
103
|
|
|
*/ |
104
|
1 |
|
public function getSortParameters() |
105
|
|
|
{ |
106
|
1 |
|
return $this->sortParameters; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Parse sorting parameters string |
111
|
|
|
* |
112
|
|
|
* @param string $value |
113
|
|
|
* @return array |
114
|
|
|
*/ |
115
|
3 |
|
public function parseSortingParameters($value) |
116
|
|
|
{ |
117
|
3 |
|
if (empty($value)) { |
118
|
1 |
|
return null; |
119
|
|
|
} |
120
|
|
|
|
121
|
3 |
|
if (!is_string($value)) { |
122
|
1 |
|
throw new InvalidArgumentException('Sorting parameters must be a string', 400); |
123
|
|
|
} |
124
|
|
|
|
125
|
2 |
|
$sorting = []; |
126
|
|
|
|
127
|
2 |
|
$fields = explode(',', $value); |
128
|
2 |
|
foreach ($fields as $field) { |
129
|
2 |
|
$isAsc = ('-' !== $field[0]) ? true : false; |
130
|
2 |
|
if (false === $isAsc) { |
131
|
2 |
|
$field = mb_substr($field, 1); |
132
|
2 |
|
} |
133
|
|
|
|
134
|
2 |
|
$sorting[] = new SortParameter($field, $isAsc); |
135
|
2 |
|
} |
136
|
|
|
|
137
|
2 |
|
return (!empty($sorting)) ? $sorting : null; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
/** |
141
|
|
|
* Validate sort parameters |
142
|
|
|
* |
143
|
|
|
* @param ExecutionContextInterface $context |
144
|
|
|
* @Assert\Callback() |
145
|
|
|
*/ |
146
|
1 |
|
public function validateSortParameters(ExecutionContextInterface $context) |
147
|
|
|
{ |
148
|
1 |
|
if (empty($this->sortParameters)) { |
149
|
1 |
|
return; |
150
|
|
|
} |
151
|
|
|
|
152
|
1 |
|
$fields = []; |
153
|
1 |
|
foreach ($this->sortParameters as $parameter) { |
154
|
1 |
|
$fields[] = $parameter->getField(); |
155
|
1 |
|
} |
156
|
|
|
|
157
|
1 |
|
$invalidFields = array_diff($fields, $this->getSortableFields()); |
158
|
1 |
|
if (count($invalidFields) > 0) { |
|
|
|
|
159
|
1 |
|
$this->addViolation( |
160
|
1 |
|
$context, |
161
|
1 |
|
'Sorting by following fields is not supported: %fields%', |
162
|
1 |
|
['%fields%' => sprintf("'%s'", implode("', '", $invalidFields))], |
163
|
1 |
|
$invalidFields, |
164
|
1 |
|
self::INVALID_SORTING, |
165
|
1 |
|
'sortParameters', |
166
|
1 |
|
count($invalidFields) |
167
|
1 |
|
); |
168
|
1 |
|
} |
169
|
1 |
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Validate page size |
173
|
|
|
* |
174
|
|
|
* @param ExecutionContextInterface $context |
175
|
|
|
* @Assert\Callback() |
176
|
|
|
*/ |
177
|
1 |
|
public function validatePageSize(ExecutionContextInterface $context) |
178
|
|
|
{ |
179
|
1 |
|
if (!empty($this->pageSize) && |
180
|
1 |
|
(null !== ($maxSize = $this->getMaxPageSize())) && |
181
|
1 |
|
($this->pageSize > $maxSize) |
182
|
1 |
|
) { |
183
|
1 |
|
$this->addViolation( |
184
|
1 |
|
$context, |
185
|
1 |
|
'Page size must be leas or equal than %size%', |
186
|
1 |
|
['%size%' => $maxSize], |
187
|
1 |
|
$this->pageSize, |
188
|
1 |
|
self::INVALID_PAGE_SIZE, |
189
|
|
|
'pageSize' |
190
|
1 |
|
); |
191
|
1 |
|
} |
192
|
1 |
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Returns default page size |
196
|
|
|
* |
197
|
|
|
* @return int|null |
198
|
|
|
*/ |
199
|
1 |
|
protected function getDefaultPageSize() |
200
|
|
|
{ |
201
|
1 |
|
return 10; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Returns max page size |
206
|
|
|
* |
207
|
|
|
* @return int|null |
208
|
|
|
*/ |
209
|
1 |
|
protected function getMaxPageSize() |
210
|
|
|
{ |
211
|
1 |
|
return 100; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* Returns list of supported sorting fields |
216
|
|
|
* |
217
|
|
|
* @return string[] |
218
|
|
|
*/ |
219
|
1 |
|
protected function getSortableFields() |
220
|
|
|
{ |
221
|
1 |
|
return []; |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
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.