|
1
|
|
|
<?php namespace Limoncello\Flute\Http\Query; |
|
2
|
|
|
|
|
3
|
|
|
/** |
|
4
|
|
|
* Copyright 2015-2017 [email protected] |
|
5
|
|
|
* |
|
6
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
7
|
|
|
* you may not use this file except in compliance with the License. |
|
8
|
|
|
* You may obtain a copy of the License at |
|
9
|
|
|
* |
|
10
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
11
|
|
|
* |
|
12
|
|
|
* Unless required by applicable law or agreed to in writing, software |
|
13
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
14
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
15
|
|
|
* See the License for the specific language governing permissions and |
|
16
|
|
|
* limitations under the License. |
|
17
|
|
|
*/ |
|
18
|
|
|
|
|
19
|
|
|
use Generator; |
|
20
|
|
|
use Limoncello\Flute\Contracts\Api\CrudInterface; |
|
21
|
|
|
use Limoncello\Flute\Contracts\Http\Query\AttributeInterface; |
|
22
|
|
|
use Limoncello\Flute\Contracts\Http\Query\FilterParameterInterface; |
|
23
|
|
|
use Limoncello\Flute\Contracts\Http\Query\ParametersMapperInterface; |
|
24
|
|
|
use Limoncello\Flute\Contracts\Http\Query\RelationshipInterface; |
|
25
|
|
|
use Limoncello\Flute\Contracts\Schema\JsonSchemasInterface; |
|
26
|
|
|
use Limoncello\Flute\Contracts\Schema\SchemaInterface; |
|
27
|
|
|
use Limoncello\Flute\Contracts\Validation\JsonApiQueryValidatingParserInterface; |
|
28
|
|
|
use Limoncello\Flute\Exceptions\InvalidQueryParametersException; |
|
29
|
|
|
use Limoncello\Flute\Exceptions\LogicException; |
|
30
|
|
|
use Neomerx\JsonApi\Contracts\Http\Query\BaseQueryParserInterface; |
|
31
|
|
|
use Neomerx\JsonApi\Document\Error; |
|
32
|
|
|
|
|
33
|
|
|
/** |
|
34
|
|
|
* @package Limoncello\Flute |
|
35
|
|
|
* |
|
36
|
|
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
|
37
|
|
|
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity) |
|
38
|
|
|
*/ |
|
39
|
|
|
class ParametersMapper implements ParametersMapperInterface |
|
40
|
|
|
{ |
|
41
|
|
|
/** Message */ |
|
42
|
|
|
public const MSG_ERR_INVALID_OPERATION = 'Invalid Operation.'; |
|
43
|
|
|
|
|
44
|
|
|
/** Message */ |
|
45
|
|
|
public const MSG_ERR_INVALID_FIELD = 'Invalid field.'; |
|
46
|
|
|
|
|
47
|
|
|
/** Message */ |
|
48
|
|
|
public const MSG_ERR_ROOT_SCHEMA_IS_NOT_SET = 'Root Schema is not set.'; |
|
49
|
|
|
|
|
50
|
|
|
/** Message */ |
|
51
|
|
|
private const MSG_PARAM_INCLUDE = BaseQueryParserInterface::PARAM_INCLUDE; |
|
52
|
|
|
|
|
53
|
|
|
/** Message */ |
|
54
|
|
|
private const MSG_PARAM_FILTER = BaseQueryParserInterface::PARAM_FILTER; |
|
55
|
|
|
|
|
56
|
|
|
/** internal constant */ |
|
57
|
|
|
private const REL_FILTER_INDEX = 0; |
|
58
|
|
|
|
|
59
|
|
|
/** internal constant */ |
|
60
|
|
|
private const REL_SORT_INDEX = 1; |
|
61
|
|
|
|
|
62
|
|
|
/** |
|
63
|
|
|
* @var SchemaInterface |
|
64
|
|
|
*/ |
|
65
|
|
|
private $rootSchema; |
|
66
|
|
|
|
|
67
|
|
|
/** |
|
68
|
|
|
* @var JsonSchemasInterface |
|
69
|
|
|
*/ |
|
70
|
|
|
private $jsonSchemas; |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* @var array|null |
|
74
|
|
|
*/ |
|
75
|
|
|
private $messages; |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* @var iterable |
|
79
|
|
|
*/ |
|
80
|
|
|
private $filters; |
|
81
|
|
|
|
|
82
|
|
|
/** |
|
83
|
|
|
* @var iterable |
|
84
|
|
|
*/ |
|
85
|
|
|
private $sorts; |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* @var iterable |
|
89
|
|
|
*/ |
|
90
|
|
|
private $includes; |
|
91
|
|
|
|
|
92
|
|
|
/** |
|
93
|
|
|
* @param JsonSchemasInterface $jsonSchemas |
|
94
|
|
|
* @param array|null $messages |
|
95
|
|
|
*/ |
|
96
|
25 |
|
public function __construct(JsonSchemasInterface $jsonSchemas, array $messages = null) |
|
97
|
|
|
{ |
|
98
|
25 |
|
$this->jsonSchemas = $jsonSchemas; |
|
99
|
25 |
|
$this->messages = $messages; |
|
100
|
|
|
|
|
101
|
25 |
|
$this->withoutFilters()->withoutSorts()->withoutIncludes(); |
|
102
|
|
|
} |
|
103
|
|
|
|
|
104
|
|
|
/** |
|
105
|
|
|
* @inheritdoc |
|
106
|
|
|
*/ |
|
107
|
23 |
|
public function selectRootSchemaByResourceType(string $resourceType): ParametersMapperInterface |
|
108
|
|
|
{ |
|
109
|
23 |
|
$this->rootSchema = $this->getJsonSchemas()->getSchemaByResourceType($resourceType); |
|
110
|
|
|
|
|
111
|
23 |
|
return $this; |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* @inheritdoc |
|
116
|
|
|
*/ |
|
117
|
25 |
|
public function withFilters(iterable $filters): ParametersMapperInterface |
|
118
|
|
|
{ |
|
119
|
|
|
// Sample format |
|
120
|
|
|
// [ |
|
121
|
|
|
// 'attribute' => [ |
|
|
|
|
|
|
122
|
|
|
// 'op1' => [arg1], |
|
|
|
|
|
|
123
|
|
|
// 'op2' => [arg1, arg1], |
|
|
|
|
|
|
124
|
|
|
// ], |
|
125
|
|
|
// 'relationship' => [ |
|
|
|
|
|
|
126
|
|
|
// 'op1' => [arg1], |
|
|
|
|
|
|
127
|
|
|
// 'op2' => [arg1, arg1], |
|
|
|
|
|
|
128
|
|
|
// ], |
|
129
|
|
|
// 'relationship.attribute' => [ |
|
|
|
|
|
|
130
|
|
|
// 'op1' => [arg1], |
|
|
|
|
|
|
131
|
|
|
// 'op2' => [arg1, arg1], |
|
|
|
|
|
|
132
|
|
|
// ], |
|
133
|
|
|
// ]; |
|
134
|
|
|
|
|
135
|
25 |
|
$this->filters = $filters; |
|
|
|
|
|
|
136
|
|
|
|
|
137
|
25 |
|
return $this; |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** |
|
141
|
|
|
* @return self |
|
142
|
|
|
*/ |
|
143
|
25 |
|
public function withoutFilters(): self |
|
144
|
|
|
{ |
|
145
|
25 |
|
$this->withFilters([]); |
|
|
|
|
|
|
146
|
|
|
|
|
147
|
25 |
|
return $this; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* @inheritdoc |
|
152
|
|
|
*/ |
|
153
|
25 |
|
public function withSorts(iterable $sorts): ParametersMapperInterface |
|
154
|
|
|
{ |
|
155
|
|
|
// Sample format (name => isAsc) |
|
156
|
|
|
// [ |
|
157
|
|
|
// 'attribute' => true, |
|
|
|
|
|
|
158
|
|
|
// 'relationship' => false, |
|
|
|
|
|
|
159
|
|
|
// 'relationship.attribute' => true, |
|
|
|
|
|
|
160
|
|
|
// ]; |
|
161
|
|
|
|
|
162
|
25 |
|
$this->sorts = $sorts; |
|
|
|
|
|
|
163
|
|
|
|
|
164
|
25 |
|
return $this; |
|
165
|
|
|
} |
|
166
|
|
|
|
|
167
|
|
|
/** |
|
168
|
|
|
* @return self |
|
169
|
|
|
*/ |
|
170
|
25 |
|
public function withoutSorts(): self |
|
171
|
|
|
{ |
|
172
|
25 |
|
$this->withSorts([]); |
|
|
|
|
|
|
173
|
|
|
|
|
174
|
25 |
|
return $this; |
|
175
|
|
|
} |
|
176
|
|
|
|
|
177
|
|
|
/** |
|
178
|
|
|
* @inheritdoc |
|
179
|
|
|
*/ |
|
180
|
25 |
|
public function withIncludes(iterable $includes): ParametersMapperInterface |
|
181
|
|
|
{ |
|
182
|
|
|
// Sample format |
|
183
|
|
|
// [ |
|
184
|
|
|
// ['relationship'], |
|
185
|
|
|
// ['relationship', 'next_relationship'], |
|
|
|
|
|
|
186
|
|
|
// ]; |
|
187
|
|
|
|
|
188
|
25 |
|
$this->includes = $includes; |
|
|
|
|
|
|
189
|
|
|
|
|
190
|
25 |
|
return $this; |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
/** |
|
194
|
|
|
* @return self |
|
195
|
|
|
*/ |
|
196
|
25 |
|
public function withoutIncludes(): self |
|
197
|
|
|
{ |
|
198
|
25 |
|
$this->withIncludes([]); |
|
|
|
|
|
|
199
|
|
|
|
|
200
|
25 |
|
return $this; |
|
201
|
|
|
} |
|
202
|
|
|
|
|
203
|
|
|
/** |
|
204
|
|
|
* @inheritdoc |
|
205
|
|
|
*/ |
|
206
|
18 |
|
public function getMappedFilters(): iterable |
|
207
|
|
|
{ |
|
208
|
18 |
|
foreach ($this->getFilters() as $field => $operationsAndArgs) { |
|
209
|
10 |
|
assert(is_string($field)); |
|
210
|
|
|
|
|
211
|
|
|
/** @var RelationshipInterface|null $relationship */ |
|
212
|
|
|
/** @var AttributeInterface $attribute */ |
|
213
|
10 |
|
list ($relationship, $attribute) = $this->mapToRelationshipAndAttribute($field); |
|
214
|
|
|
|
|
215
|
9 |
|
$filter = new FilterParameter( |
|
216
|
9 |
|
$attribute, |
|
217
|
9 |
|
$this->parseOperationsAndArguments(static::MSG_PARAM_FILTER, $operationsAndArgs), |
|
|
|
|
|
|
218
|
9 |
|
$relationship |
|
219
|
|
|
); |
|
220
|
|
|
|
|
221
|
9 |
|
yield $filter; |
|
222
|
|
|
} |
|
223
|
|
|
} |
|
224
|
|
|
|
|
225
|
|
|
/** |
|
226
|
|
|
* @inheritdoc |
|
227
|
|
|
*/ |
|
228
|
15 |
|
public function getMappedSorts(): iterable |
|
229
|
|
|
{ |
|
230
|
15 |
|
foreach ($this->getSorts() as $field => $isAsc) { |
|
231
|
5 |
|
assert(is_string($field) === true && empty($field) === false && is_bool($isAsc) === true); |
|
232
|
|
|
|
|
233
|
|
|
/** @var RelationshipInterface|null $relationship */ |
|
234
|
|
|
/** @var AttributeInterface $attribute */ |
|
235
|
5 |
|
list ($relationship, $attribute) = $this->mapToRelationshipAndAttribute($field); |
|
236
|
|
|
|
|
237
|
5 |
|
$sort = new SortParameter($attribute, $isAsc, $relationship); |
|
238
|
|
|
|
|
239
|
5 |
|
yield $sort; |
|
240
|
|
|
} |
|
241
|
|
|
} |
|
242
|
|
|
|
|
243
|
|
|
/** |
|
244
|
|
|
* @inheritdoc |
|
245
|
|
|
*/ |
|
246
|
17 |
|
public function getMappedIncludes(): iterable |
|
247
|
|
|
{ |
|
248
|
17 |
|
$fromSchema = $this->getRootSchema(); |
|
249
|
16 |
|
$getMappedRelLinks = function (iterable $links) use ($fromSchema) : iterable { |
|
250
|
5 |
|
foreach ($links as $link) { |
|
251
|
5 |
|
assert(is_string($link)); |
|
252
|
5 |
|
$fromSchemaClass = get_class($fromSchema); |
|
253
|
5 |
|
if ($this->getJsonSchemas()->hasRelationshipSchema($fromSchemaClass, $link)) { |
|
254
|
4 |
|
$toSchema = $this->getJsonSchemas()->getRelationshipSchema($fromSchemaClass, $link); |
|
255
|
4 |
|
$relationship = new Relationship($link, $fromSchema, $toSchema); |
|
256
|
|
|
|
|
257
|
4 |
|
yield $relationship; |
|
258
|
|
|
|
|
259
|
4 |
|
$fromSchema = $toSchema; |
|
|
|
|
|
|
260
|
4 |
|
continue; |
|
261
|
|
|
} |
|
262
|
|
|
|
|
263
|
1 |
|
$error = $this->createQueryError(static::MSG_PARAM_INCLUDE, static::MSG_ERR_INVALID_FIELD); |
|
264
|
1 |
|
throw new InvalidQueryParametersException($error); |
|
265
|
|
|
} |
|
266
|
16 |
|
}; |
|
267
|
|
|
|
|
268
|
16 |
|
foreach ($this->getIncludes() as $links) { |
|
269
|
5 |
|
yield $getMappedRelLinks($links); |
|
270
|
|
|
} |
|
271
|
|
|
} |
|
272
|
|
|
|
|
273
|
|
|
/** |
|
274
|
|
|
* @inheritdoc |
|
275
|
|
|
* |
|
276
|
|
|
* @SuppressWarnings(PHPMD.ElseExpression) |
|
277
|
|
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) |
|
278
|
|
|
* @SuppressWarnings(PHPMD.NPathComplexity) |
|
279
|
|
|
*/ |
|
280
|
15 |
|
public function applyQueryParameters( |
|
281
|
|
|
JsonApiQueryValidatingParserInterface $parser, |
|
282
|
|
|
CrudInterface $api |
|
283
|
|
|
): CrudInterface { |
|
284
|
|
|
// |
|
285
|
|
|
// Paging |
|
286
|
|
|
// |
|
287
|
15 |
|
$api->withPaging($parser->getPagingOffset(), $parser->getPagingLimit()); |
|
288
|
|
|
|
|
289
|
|
|
// |
|
290
|
|
|
// Includes |
|
291
|
|
|
// |
|
292
|
|
|
|
|
293
|
|
|
// the two functions below compose a 2D array of relationship names in a form of iterable |
|
294
|
|
|
// [ |
|
295
|
|
|
// ['rel1_name1', 'rel1_name2', 'rel1_name3', ], |
|
|
|
|
|
|
296
|
|
|
// ['rel2_name1', ], |
|
|
|
|
|
|
297
|
|
|
// ['rel3_name1', 'rel3_name2', 'rel3_name3', ], |
|
|
|
|
|
|
298
|
|
|
// ] |
|
299
|
15 |
|
$includeAsModelNames = function (iterable $relationships): iterable { |
|
300
|
3 |
|
foreach ($relationships as $relationship) { |
|
301
|
3 |
|
assert($relationship instanceof RelationshipInterface); |
|
302
|
3 |
|
yield $relationship->getNameInModel(); |
|
303
|
|
|
} |
|
304
|
15 |
|
}; |
|
305
|
15 |
|
$mappedIncludes = $this->getMappedIncludes(); |
|
306
|
15 |
|
$getIncludes = function () use ($mappedIncludes, $includeAsModelNames) : iterable { |
|
307
|
14 |
|
foreach ($mappedIncludes as $relationships) { |
|
308
|
3 |
|
yield $includeAsModelNames($relationships); |
|
309
|
|
|
} |
|
310
|
15 |
|
}; |
|
311
|
|
|
|
|
312
|
|
|
// |
|
313
|
|
|
// Filters and Sorts |
|
314
|
|
|
// |
|
315
|
|
|
|
|
316
|
15 |
|
$parser->areFiltersWithAnd() === true ? $api->combineWithAnd() : $api->combineWithOr(); |
|
317
|
|
|
|
|
318
|
|
|
$this |
|
319
|
15 |
|
->withFilters($parser->getFilters()) |
|
320
|
14 |
|
->withSorts($parser->getSorts()) |
|
321
|
14 |
|
->withIncludes($parser->getIncludes()); |
|
322
|
|
|
|
|
323
|
14 |
|
$attributeFilters = []; |
|
324
|
14 |
|
$attributeSorts = []; |
|
325
|
|
|
|
|
326
|
|
|
// As relationship filters and sorts should be applied together (in one SQL JOIN) |
|
327
|
|
|
// we have to iterate through all filters and merge related to the same relationship. |
|
328
|
14 |
|
$relFiltersAndSorts = []; |
|
329
|
|
|
|
|
330
|
14 |
|
foreach ($this->getMappedFilters() as $filter) { |
|
331
|
|
|
/** @var FilterParameterInterface $filter */ |
|
332
|
6 |
|
$attributeName = $filter->getAttribute()->getNameInModel(); |
|
333
|
6 |
|
if ($filter->getRelationship() === null) { |
|
334
|
3 |
|
$attributeFilters[$attributeName] = $filter->getOperationsWithArguments(); |
|
335
|
|
|
} else { |
|
336
|
5 |
|
$relationshipName = $filter->getRelationship()->getNameInModel(); |
|
|
|
|
|
|
337
|
|
|
|
|
338
|
5 |
|
$relFiltersAndSorts[$relationshipName][self::REL_FILTER_INDEX][$attributeName] = |
|
339
|
6 |
|
$filter->getOperationsWithArguments(); |
|
340
|
|
|
} |
|
341
|
|
|
} |
|
342
|
14 |
|
foreach ($this->getMappedSorts() as $sort) { |
|
343
|
|
|
/** @var SortParameter $sort */ |
|
344
|
4 |
|
$attributeName = $sort->getAttribute()->getNameInModel(); |
|
345
|
4 |
|
if ($sort->getRelationship() === null) { |
|
346
|
3 |
|
$attributeSorts[$attributeName] = $sort->isAsc(); |
|
347
|
|
|
} else { |
|
348
|
1 |
|
$relationshipName = $sort->getRelationship()->getNameInModel(); |
|
|
|
|
|
|
349
|
|
|
|
|
350
|
4 |
|
$relFiltersAndSorts[$relationshipName][self::REL_SORT_INDEX][$attributeName] = $sort->isAsc(); |
|
351
|
|
|
} |
|
352
|
|
|
} |
|
353
|
|
|
|
|
354
|
14 |
|
$api->withFilters($attributeFilters) |
|
|
|
|
|
|
355
|
14 |
|
->withSorts($attributeSorts) |
|
356
|
14 |
|
->withIncludes($getIncludes()); |
|
357
|
|
|
|
|
358
|
14 |
|
foreach ($relFiltersAndSorts as $relationshipName => $filtersAndSorts) { |
|
359
|
5 |
|
if (array_key_exists(self::REL_FILTER_INDEX, $filtersAndSorts) === true) { |
|
360
|
5 |
|
$api->withRelationshipFilters($relationshipName, $filtersAndSorts[self::REL_FILTER_INDEX]); |
|
361
|
|
|
} |
|
362
|
5 |
|
if (array_key_exists(self::REL_SORT_INDEX, $filtersAndSorts) === true) { |
|
363
|
5 |
|
$api->withRelationshipSorts($relationshipName, $filtersAndSorts[self::REL_SORT_INDEX]); |
|
364
|
|
|
} |
|
365
|
|
|
} |
|
366
|
|
|
|
|
367
|
14 |
|
return $api; |
|
368
|
|
|
} |
|
369
|
|
|
|
|
370
|
|
|
/** |
|
371
|
|
|
* @param string $field |
|
372
|
|
|
* |
|
373
|
|
|
* @return array |
|
374
|
|
|
*/ |
|
375
|
14 |
|
private function mapToRelationshipAndAttribute(string $field): array |
|
376
|
|
|
{ |
|
377
|
14 |
|
$rootSchema = $this->getRootSchema(); |
|
378
|
14 |
|
if ($rootSchema->hasAttributeMapping($field) === true) { |
|
379
|
10 |
|
$relationship = null; |
|
380
|
10 |
|
$schema = $rootSchema; |
|
381
|
10 |
|
$attribute = new Attribute($field, $schema); |
|
382
|
|
|
|
|
383
|
10 |
|
return [$relationship, $attribute]; |
|
384
|
8 |
|
} elseif ($rootSchema->hasRelationshipMapping($field) === true) { |
|
385
|
5 |
|
$fromSchema = $rootSchema; |
|
386
|
5 |
|
$toSchema = $this->getJsonSchemas()->getRelationshipSchema(get_class($fromSchema), $field); |
|
387
|
5 |
|
$relationship = new Relationship($field, $fromSchema, $toSchema); |
|
388
|
5 |
|
$attribute = new Attribute($toSchema::RESOURCE_ID, $toSchema); |
|
389
|
|
|
|
|
390
|
5 |
|
return [$relationship, $attribute]; |
|
391
|
5 |
|
} elseif (count($mightBeRelAndAttr = explode('.', $field, 2)) === 2) { |
|
392
|
|
|
// Last chance. It could be a dot ('.') separated relationship with an attribute. |
|
393
|
|
|
|
|
394
|
4 |
|
$mightBeRel = $mightBeRelAndAttr[0]; |
|
395
|
4 |
|
$mightBeAttr = $mightBeRelAndAttr[1]; |
|
396
|
|
|
|
|
397
|
4 |
|
$fromSchema = $rootSchema; |
|
398
|
4 |
|
if ($fromSchema->hasRelationshipMapping($mightBeRel)) { |
|
399
|
4 |
|
$toSchema = $this->getJsonSchemas()->getRelationshipSchema(get_class($fromSchema), $mightBeRel); |
|
400
|
4 |
|
if ($toSchema::hasAttributeMapping($mightBeAttr) === true) { |
|
401
|
4 |
|
$relationship = new Relationship($mightBeRel, $fromSchema, $toSchema); |
|
402
|
4 |
|
$attribute = new Attribute($mightBeAttr, $toSchema); |
|
403
|
|
|
|
|
404
|
4 |
|
return [$relationship, $attribute]; |
|
405
|
|
|
} |
|
406
|
|
|
} |
|
407
|
|
|
} |
|
408
|
|
|
|
|
409
|
1 |
|
$error = $this->createQueryError($field, static::MSG_ERR_INVALID_FIELD); |
|
410
|
1 |
|
throw new InvalidQueryParametersException($error); |
|
411
|
|
|
} |
|
412
|
|
|
|
|
413
|
|
|
/** |
|
414
|
|
|
* @param string $parameterName |
|
415
|
|
|
* @param iterable $value |
|
416
|
|
|
* |
|
417
|
|
|
* @return iterable |
|
|
|
|
|
|
418
|
|
|
* |
|
419
|
|
|
* @SuppressWarnings(PHPMD.CyclomaticComplexity) |
|
420
|
|
|
*/ |
|
421
|
9 |
|
private function parseOperationsAndArguments(string $parameterName, iterable $value): iterable |
|
422
|
|
|
{ |
|
423
|
|
|
// in this case we interpret it as an [operation => [arg1, arg2]] |
|
424
|
9 |
|
foreach ($value as $operationName => $arguments) { |
|
425
|
9 |
|
assert(is_array($arguments) || $arguments instanceof Generator); |
|
426
|
|
|
|
|
427
|
|
|
switch ($operationName) { |
|
428
|
9 |
|
case '=': |
|
429
|
9 |
|
case 'eq': |
|
430
|
9 |
|
case 'equals': |
|
431
|
2 |
|
$operation = FilterParameterInterface::OPERATION_EQUALS; |
|
432
|
2 |
|
break; |
|
433
|
9 |
|
case '!=': |
|
434
|
9 |
|
case 'neq': |
|
435
|
9 |
|
case 'not-equals': |
|
436
|
1 |
|
$operation = FilterParameterInterface::OPERATION_NOT_EQUALS; |
|
437
|
1 |
|
break; |
|
438
|
9 |
|
case '<': |
|
439
|
9 |
|
case 'lt': |
|
440
|
9 |
|
case 'less-than': |
|
441
|
2 |
|
$operation = FilterParameterInterface::OPERATION_LESS_THAN; |
|
442
|
2 |
|
break; |
|
443
|
9 |
|
case '<=': |
|
444
|
9 |
|
case 'lte': |
|
445
|
9 |
|
case 'less-or-equals': |
|
446
|
1 |
|
$operation = FilterParameterInterface::OPERATION_LESS_OR_EQUALS; |
|
447
|
1 |
|
break; |
|
448
|
9 |
|
case '>': |
|
449
|
9 |
|
case 'gt': |
|
450
|
8 |
|
case 'greater-than': |
|
451
|
2 |
|
$operation = FilterParameterInterface::OPERATION_GREATER_THAN; |
|
452
|
2 |
|
break; |
|
453
|
8 |
|
case '>=': |
|
454
|
8 |
|
case 'gte': |
|
455
|
8 |
|
case 'greater-or-equals': |
|
456
|
1 |
|
$operation = FilterParameterInterface::OPERATION_GREATER_OR_EQUALS; |
|
457
|
1 |
|
break; |
|
458
|
8 |
|
case 'like': |
|
459
|
6 |
|
$operation = FilterParameterInterface::OPERATION_LIKE; |
|
460
|
6 |
|
break; |
|
461
|
6 |
|
case 'not-like': |
|
462
|
2 |
|
$operation = FilterParameterInterface::OPERATION_NOT_LIKE; |
|
463
|
2 |
|
break; |
|
464
|
6 |
|
case 'in': |
|
465
|
5 |
|
$operation = FilterParameterInterface::OPERATION_IN; |
|
466
|
5 |
|
break; |
|
467
|
2 |
|
case 'not-in': |
|
468
|
1 |
|
$operation = FilterParameterInterface::OPERATION_NOT_IN; |
|
469
|
1 |
|
break; |
|
470
|
2 |
|
case 'is-null': |
|
471
|
1 |
|
$operation = FilterParameterInterface::OPERATION_IS_NULL; |
|
472
|
1 |
|
$arguments = []; |
|
473
|
1 |
|
break; |
|
474
|
2 |
|
case 'not-null': |
|
475
|
1 |
|
$operation = FilterParameterInterface::OPERATION_IS_NOT_NULL; |
|
476
|
1 |
|
$arguments = []; |
|
477
|
1 |
|
break; |
|
478
|
|
|
default: |
|
479
|
1 |
|
$error = $this->createQueryError($parameterName, static::MSG_ERR_INVALID_OPERATION); |
|
480
|
1 |
|
throw new InvalidQueryParametersException($error); |
|
481
|
|
|
} |
|
482
|
|
|
|
|
483
|
8 |
|
yield $operation => $arguments; |
|
484
|
|
|
} |
|
485
|
|
|
} |
|
486
|
|
|
|
|
487
|
|
|
/** |
|
488
|
|
|
* @return SchemaInterface |
|
489
|
|
|
*/ |
|
490
|
22 |
|
private function getRootSchema(): SchemaInterface |
|
491
|
|
|
{ |
|
492
|
22 |
|
if ($this->rootSchema === null) { |
|
493
|
1 |
|
throw new LogicException($this->getMessage(static::MSG_ERR_ROOT_SCHEMA_IS_NOT_SET)); |
|
494
|
|
|
} |
|
495
|
|
|
|
|
496
|
21 |
|
return $this->rootSchema; |
|
497
|
|
|
} |
|
498
|
|
|
|
|
499
|
|
|
/** |
|
500
|
|
|
* @return JsonSchemasInterface |
|
501
|
|
|
*/ |
|
502
|
23 |
|
private function getJsonSchemas(): JsonSchemasInterface |
|
503
|
|
|
{ |
|
504
|
23 |
|
return $this->jsonSchemas; |
|
505
|
|
|
} |
|
506
|
|
|
|
|
507
|
|
|
/** |
|
508
|
|
|
* @return iterable |
|
509
|
|
|
*/ |
|
510
|
18 |
|
private function getFilters(): iterable |
|
511
|
|
|
{ |
|
512
|
18 |
|
return $this->filters; |
|
513
|
|
|
} |
|
514
|
|
|
|
|
515
|
|
|
/** |
|
516
|
|
|
* @return iterable |
|
517
|
|
|
*/ |
|
518
|
15 |
|
private function getSorts(): iterable |
|
519
|
|
|
{ |
|
520
|
15 |
|
return $this->sorts; |
|
521
|
|
|
} |
|
522
|
|
|
|
|
523
|
|
|
/** |
|
524
|
|
|
* @return iterable |
|
525
|
|
|
*/ |
|
526
|
16 |
|
private function getIncludes(): iterable |
|
527
|
|
|
{ |
|
528
|
16 |
|
return $this->includes; |
|
529
|
|
|
} |
|
530
|
|
|
|
|
531
|
|
|
/** |
|
532
|
|
|
* @param string $name |
|
533
|
|
|
* @param string $title |
|
534
|
|
|
* |
|
535
|
|
|
* @return Error |
|
536
|
|
|
*/ |
|
537
|
3 |
|
private function createQueryError(string $name, string $title): Error |
|
538
|
|
|
{ |
|
539
|
3 |
|
$title = $this->getMessage($title); |
|
540
|
3 |
|
$source = [Error::SOURCE_PARAMETER => $name]; |
|
541
|
3 |
|
$error = new Error(null, null, null, null, $title, null, $source); |
|
542
|
|
|
|
|
543
|
3 |
|
return $error; |
|
544
|
|
|
} |
|
545
|
|
|
|
|
546
|
|
|
/** |
|
547
|
|
|
* @param string $message |
|
548
|
|
|
* |
|
549
|
|
|
* @return string |
|
550
|
|
|
*/ |
|
551
|
4 |
|
private function getMessage(string $message): string |
|
552
|
|
|
{ |
|
553
|
4 |
|
$hasTranslation = $this->messages !== null && array_key_exists($message, $this->messages) === false; |
|
554
|
|
|
|
|
555
|
4 |
|
return $hasTranslation === true ? $this->messages[$message] : $message; |
|
556
|
|
|
} |
|
557
|
|
|
} |
|
558
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.