|
1
|
|
|
<?php namespace Limoncello\Flute\Http; |
|
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 Closure; |
|
20
|
|
|
use Limoncello\Contracts\Application\ModelInterface; |
|
21
|
|
|
use Limoncello\Contracts\Data\ModelSchemeInfoInterface; |
|
22
|
|
|
use Limoncello\Contracts\Data\RelationshipTypes; |
|
23
|
|
|
use Limoncello\Contracts\L10n\FormatterFactoryInterface; |
|
24
|
|
|
use Limoncello\Contracts\L10n\FormatterInterface; |
|
25
|
|
|
use Limoncello\Flute\Contracts\Api\CrudInterface; |
|
26
|
|
|
use Limoncello\Flute\Contracts\FactoryInterface; |
|
27
|
|
|
use Limoncello\Flute\Contracts\Http\ControllerInterface; |
|
28
|
|
|
use Limoncello\Flute\Contracts\Http\Query\ParametersMapperInterface; |
|
29
|
|
|
use Limoncello\Flute\Contracts\Http\Query\QueryParserInterface; |
|
30
|
|
|
use Limoncello\Flute\Contracts\Models\PaginatedDataInterface; |
|
31
|
|
|
use Limoncello\Flute\Contracts\Schema\SchemaInterface; |
|
32
|
|
|
use Limoncello\Flute\Contracts\Validation\JsonApiValidatorFactoryInterface; |
|
33
|
|
|
use Limoncello\Flute\Contracts\Validation\JsonApiValidatorInterface; |
|
34
|
|
|
use Limoncello\Flute\Http\Traits\CreateResponsesTrait; |
|
35
|
|
|
use Limoncello\Flute\L10n\Messages; |
|
36
|
|
|
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as DI; |
|
37
|
|
|
use Neomerx\JsonApi\Exceptions\JsonApiException; |
|
38
|
|
|
use Psr\Container\ContainerExceptionInterface; |
|
39
|
|
|
use Psr\Container\ContainerInterface; |
|
40
|
|
|
use Psr\Container\NotFoundExceptionInterface; |
|
41
|
|
|
use Psr\Http\Message\ResponseInterface; |
|
42
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
|
43
|
|
|
|
|
44
|
|
|
/** |
|
45
|
|
|
* @package Limoncello\Flute |
|
46
|
|
|
* |
|
47
|
|
|
* @SuppressWarnings(PHPMD.TooManyMethods) |
|
48
|
|
|
* @SuppressWarnings(PHPMD.CouplingBetweenObjects) |
|
49
|
|
|
*/ |
|
50
|
|
|
abstract class BaseController implements ControllerInterface |
|
51
|
|
|
{ |
|
52
|
|
|
use CreateResponsesTrait; |
|
53
|
|
|
|
|
54
|
|
|
/** API class name */ |
|
55
|
|
|
const API_CLASS = null; |
|
56
|
|
|
|
|
57
|
|
|
/** JSON API Schema class name */ |
|
58
|
|
|
const SCHEMA_CLASS = null; |
|
59
|
|
|
|
|
60
|
|
|
/** JSON API validation rules set class */ |
|
61
|
|
|
const ON_CREATE_VALIDATION_RULES_SET_CLASS = null; |
|
62
|
|
|
|
|
63
|
|
|
/** JSON API validation rules set class */ |
|
64
|
|
|
const ON_UPDATE_VALIDATION_RULES_SET_CLASS = null; |
|
65
|
|
|
|
|
66
|
|
|
/** |
|
67
|
|
|
* @inheritdoc |
|
68
|
|
|
*/ |
|
69
|
12 |
|
public static function index( |
|
70
|
|
|
array $routeParams, |
|
71
|
|
|
ContainerInterface $container, |
|
72
|
|
|
ServerRequestInterface $request |
|
73
|
|
|
): ResponseInterface { |
|
74
|
|
|
// By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
|
75
|
12 |
|
$parser = static::configureOnIndexParser( |
|
76
|
12 |
|
static::createQueryParser($container) |
|
77
|
12 |
|
)->parse($request->getQueryParams()); |
|
78
|
11 |
|
$mapper = static::createParameterMapper($container); |
|
79
|
11 |
|
$api = static::createApi($container); |
|
80
|
|
|
|
|
81
|
11 |
|
$models = $mapper->applyQueryParameters($parser, $api)->index(); |
|
82
|
|
|
|
|
83
|
11 |
|
$responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
84
|
11 |
|
$response = ($models->getData()) === null ? |
|
85
|
11 |
|
$responses->getCodeResponse(404) : $responses->getContentResponse($models); |
|
86
|
|
|
|
|
87
|
11 |
|
return $response; |
|
88
|
|
|
} |
|
89
|
|
|
|
|
90
|
|
|
/** |
|
91
|
|
|
* @inheritdoc |
|
92
|
|
|
*/ |
|
93
|
1 |
|
public static function create( |
|
94
|
|
|
array $routeParams, |
|
95
|
|
|
ContainerInterface $container, |
|
96
|
|
|
ServerRequestInterface $request |
|
97
|
|
|
): ResponseInterface { |
|
98
|
1 |
|
list ($index, $api) = static::createImpl($container, $request); |
|
99
|
|
|
|
|
100
|
1 |
|
$data = $api->read($index); |
|
101
|
1 |
|
assert(!($data instanceof PaginatedDataInterface)); |
|
102
|
|
|
|
|
103
|
1 |
|
$response = static::createResponses($container, $request)->getCreatedResponse($data); |
|
104
|
|
|
|
|
105
|
1 |
|
return $response; |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
/** |
|
109
|
|
|
* @inheritdoc |
|
110
|
|
|
*/ |
|
111
|
1 |
|
public static function read( |
|
112
|
|
|
array $routeParams, |
|
113
|
|
|
ContainerInterface $container, |
|
114
|
|
|
ServerRequestInterface $request |
|
115
|
|
|
): ResponseInterface { |
|
116
|
|
|
// By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
|
117
|
1 |
|
$parser = static::configureOnReadParser( |
|
118
|
1 |
|
static::createQueryParser($container) |
|
119
|
1 |
|
)->parse($request->getQueryParams()); |
|
120
|
1 |
|
$mapper = static::createParameterMapper($container); |
|
121
|
|
|
|
|
122
|
1 |
|
$index = $routeParams[static::ROUTE_KEY_INDEX]; |
|
123
|
1 |
|
$modelData = $mapper->applyQueryParameters($parser, static::createApi($container))->read($index); |
|
124
|
1 |
|
assert(!($modelData instanceof PaginatedDataInterface)); |
|
125
|
|
|
|
|
126
|
1 |
|
$responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
127
|
1 |
|
$response = $modelData === null ? |
|
128
|
1 |
|
$responses->getCodeResponse(404) : $responses->getContentResponse($modelData); |
|
129
|
|
|
|
|
130
|
1 |
|
return $response; |
|
131
|
|
|
} |
|
132
|
|
|
|
|
133
|
|
|
/** |
|
134
|
|
|
* @inheritdoc |
|
135
|
|
|
*/ |
|
136
|
5 |
View Code Duplication |
public static function update( |
|
|
|
|
|
|
137
|
|
|
array $routeParams, |
|
138
|
|
|
ContainerInterface $container, |
|
139
|
|
|
ServerRequestInterface $request |
|
140
|
|
|
): ResponseInterface { |
|
141
|
5 |
|
list ($updated, $index, $api) = static::updateImpl($routeParams, $container, $request); |
|
142
|
|
|
|
|
143
|
2 |
|
$responses = static::createResponses($container, $request); |
|
144
|
2 |
|
if ($updated > 0) { |
|
145
|
1 |
|
$modelData = $api->read($index); |
|
146
|
1 |
|
assert(!($modelData instanceof PaginatedDataInterface)); |
|
147
|
|
|
|
|
148
|
1 |
|
return $responses->getContentResponse($modelData); |
|
149
|
|
|
} |
|
150
|
|
|
|
|
151
|
1 |
|
return $responses->getCodeResponse(404); |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
|
|
/** |
|
155
|
|
|
* @inheritdoc |
|
156
|
|
|
*/ |
|
157
|
1 |
|
public static function delete( |
|
158
|
|
|
array $routeParams, |
|
159
|
|
|
ContainerInterface $container, |
|
160
|
|
|
ServerRequestInterface $request |
|
161
|
|
|
): ResponseInterface { |
|
162
|
1 |
|
static::createApi($container)->remove($routeParams[static::ROUTE_KEY_INDEX]); |
|
163
|
|
|
|
|
164
|
1 |
|
$response = static::createResponses($container, $request)->getCodeResponse(204); |
|
165
|
|
|
|
|
166
|
1 |
|
return $response; |
|
167
|
|
|
} |
|
168
|
|
|
|
|
169
|
|
|
/** @deprecated Use `readRelationshipWithClosure` instead |
|
170
|
|
|
* @param string $index |
|
171
|
|
|
* @param string $relationshipName |
|
172
|
|
|
* @param ContainerInterface $container |
|
173
|
|
|
* @param ServerRequestInterface $request |
|
174
|
|
|
* |
|
175
|
|
|
* @return ResponseInterface |
|
176
|
|
|
* |
|
177
|
|
|
* @throws ContainerExceptionInterface |
|
178
|
|
|
* @throws NotFoundExceptionInterface |
|
179
|
|
|
*/ |
|
180
|
|
View Code Duplication |
protected static function readRelationship( |
|
|
|
|
|
|
181
|
|
|
string $index, |
|
182
|
|
|
string $relationshipName, |
|
183
|
|
|
ContainerInterface $container, |
|
184
|
|
|
ServerRequestInterface $request |
|
185
|
|
|
): ResponseInterface { |
|
186
|
2 |
|
$apiHandler = function (CrudInterface $crud) use ($index, $relationshipName) { |
|
187
|
2 |
|
return $crud->readRelationship($index, $relationshipName); |
|
188
|
2 |
|
}; |
|
189
|
|
|
|
|
190
|
2 |
|
return static::readRelationshipWithClosure($apiHandler, $relationshipName, $container, $request); |
|
191
|
|
|
} |
|
192
|
|
|
|
|
193
|
|
|
/** @deprecated Use `readRelationshipIdentifiersWithClosure` instead |
|
194
|
|
|
* @param string $index |
|
195
|
|
|
* @param string $relationshipName |
|
196
|
|
|
* @param ContainerInterface $container |
|
197
|
|
|
* @param ServerRequestInterface $request |
|
198
|
|
|
* |
|
199
|
|
|
* @return ResponseInterface |
|
200
|
|
|
* |
|
201
|
|
|
* @throws ContainerExceptionInterface |
|
202
|
|
|
* @throws NotFoundExceptionInterface |
|
203
|
|
|
*/ |
|
204
|
|
View Code Duplication |
protected static function readRelationshipIdentifiers( |
|
|
|
|
|
|
205
|
|
|
string $index, |
|
206
|
|
|
string $relationshipName, |
|
207
|
|
|
ContainerInterface $container, |
|
208
|
|
|
ServerRequestInterface $request |
|
209
|
|
|
): ResponseInterface { |
|
210
|
1 |
|
$apiHandler = function (CrudInterface $crud) use ($index, $relationshipName) { |
|
211
|
1 |
|
return $crud->readRelationship($index, $relationshipName); |
|
212
|
1 |
|
}; |
|
213
|
|
|
|
|
214
|
1 |
|
return static::readRelationshipIdentifiersWithClosure($apiHandler, $relationshipName, $container, $request); |
|
215
|
|
|
} |
|
216
|
|
|
|
|
217
|
|
|
/** |
|
218
|
|
|
* @param Closure $apiHandler |
|
219
|
|
|
* @param string $relationshipName |
|
220
|
|
|
* @param ContainerInterface $container |
|
221
|
|
|
* @param ServerRequestInterface $request |
|
222
|
|
|
* |
|
223
|
|
|
* @return ResponseInterface |
|
224
|
|
|
* |
|
225
|
|
|
* @throws ContainerExceptionInterface |
|
226
|
|
|
* @throws NotFoundExceptionInterface |
|
227
|
|
|
*/ |
|
228
|
2 |
View Code Duplication |
protected static function readRelationshipWithClosure( |
|
|
|
|
|
|
229
|
|
|
Closure $apiHandler, |
|
230
|
|
|
string $relationshipName, |
|
231
|
|
|
ContainerInterface $container, |
|
232
|
|
|
ServerRequestInterface $request |
|
233
|
|
|
): ResponseInterface { |
|
234
|
|
|
// By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
|
235
|
2 |
|
$parser = static::configureOnReadRelationshipParser( |
|
236
|
2 |
|
$relationshipName, |
|
237
|
2 |
|
static::createQueryParser($container) |
|
238
|
2 |
|
)->parse($request->getQueryParams()); |
|
239
|
|
|
|
|
240
|
2 |
|
$mapper = static::createParameterMapper($container); |
|
241
|
2 |
|
$api = $mapper->applyQueryParameters($parser, static::createApi($container)); |
|
242
|
2 |
|
$relData = call_user_func($apiHandler, $api, $container); |
|
243
|
|
|
|
|
244
|
2 |
|
$responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
245
|
2 |
|
$response = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null) ? |
|
246
|
2 |
|
$responses->getCodeResponse(404) : $responses->getContentResponse($relData); |
|
247
|
|
|
|
|
248
|
2 |
|
return $response; |
|
249
|
|
|
} |
|
250
|
|
|
|
|
251
|
|
|
/** |
|
252
|
|
|
* @param Closure $apiHandler |
|
253
|
|
|
* @param string $relationshipName |
|
254
|
|
|
* @param ContainerInterface $container |
|
255
|
|
|
* @param ServerRequestInterface $request |
|
256
|
|
|
* |
|
257
|
|
|
* @return ResponseInterface |
|
258
|
|
|
* |
|
259
|
|
|
* @throws ContainerExceptionInterface |
|
260
|
|
|
* @throws NotFoundExceptionInterface |
|
261
|
|
|
*/ |
|
262
|
1 |
View Code Duplication |
protected static function readRelationshipIdentifiersWithClosure( |
|
|
|
|
|
|
263
|
|
|
Closure $apiHandler, |
|
264
|
|
|
string $relationshipName, |
|
265
|
|
|
ContainerInterface $container, |
|
266
|
|
|
ServerRequestInterface $request |
|
267
|
|
|
): ResponseInterface { |
|
268
|
|
|
// By default no filters, sorts or includes are allowed from query. You can override this method to change it. |
|
269
|
1 |
|
$parser = static::configureOnReadRelationshipIdentifiersParser( |
|
270
|
1 |
|
$relationshipName, |
|
271
|
1 |
|
static::createQueryParser($container) |
|
272
|
1 |
|
)->parse($request->getQueryParams()); |
|
273
|
|
|
|
|
274
|
1 |
|
$mapper = static::createParameterMapper($container); |
|
275
|
1 |
|
$api = $mapper->applyQueryParameters($parser, static::createApi($container)); |
|
276
|
1 |
|
$relData = call_user_func($apiHandler, $api, $container); |
|
277
|
|
|
|
|
278
|
1 |
|
$responses = static::createResponses($container, $request, $parser->createEncodingParameters()); |
|
279
|
1 |
|
$response = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null) ? |
|
280
|
1 |
|
$responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData); |
|
281
|
|
|
|
|
282
|
1 |
|
return $response; |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
/** |
|
286
|
|
|
* @param ContainerInterface $container |
|
287
|
|
|
* @param string|null $class |
|
288
|
|
|
* |
|
289
|
|
|
* @return CrudInterface |
|
290
|
|
|
* |
|
291
|
|
|
* @throws ContainerExceptionInterface |
|
292
|
|
|
* @throws NotFoundExceptionInterface |
|
293
|
|
|
*/ |
|
294
|
22 |
|
protected static function createApi(ContainerInterface $container, string $class = null): CrudInterface |
|
295
|
|
|
{ |
|
296
|
|
|
/** @var FactoryInterface $factory */ |
|
297
|
22 |
|
$factory = $container->get(FactoryInterface::class); |
|
298
|
22 |
|
$api = $factory->createApi($class ?? static::API_CLASS); |
|
299
|
|
|
|
|
300
|
22 |
|
return $api; |
|
301
|
|
|
} |
|
302
|
|
|
|
|
303
|
|
|
/** |
|
304
|
|
|
* @param ContainerInterface $container |
|
305
|
|
|
* @param ServerRequestInterface $request |
|
306
|
|
|
* |
|
307
|
|
|
* @return array |
|
308
|
|
|
* |
|
309
|
|
|
* @throws ContainerExceptionInterface |
|
310
|
|
|
* @throws NotFoundExceptionInterface |
|
311
|
|
|
*/ |
|
312
|
8 |
|
protected static function readJsonFromRequest(ContainerInterface $container, ServerRequestInterface $request): array |
|
313
|
|
|
{ |
|
314
|
8 |
|
$body = (string)$request->getBody(); |
|
315
|
8 |
|
if (empty($body) === true || ($json = json_decode($body, true)) === null) { |
|
316
|
|
|
/** @var FactoryInterface $factory */ |
|
317
|
1 |
|
$factory = $container->get(FactoryInterface::class); |
|
318
|
1 |
|
$errors = $factory->createErrorCollection(); |
|
319
|
1 |
|
$errors->addDataError( |
|
320
|
1 |
|
static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT) |
|
321
|
|
|
); |
|
322
|
|
|
|
|
323
|
1 |
|
throw new JsonApiException($errors); |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
7 |
|
return $json; |
|
327
|
|
|
} |
|
328
|
|
|
|
|
329
|
|
|
/** |
|
330
|
|
|
* @param array $routeParams |
|
331
|
|
|
* @param ContainerInterface $container |
|
332
|
|
|
* @param array $jsonData |
|
333
|
|
|
* |
|
334
|
|
|
* @return array |
|
335
|
|
|
* @SuppressWarnings(PHPMD.ElseExpression) |
|
336
|
|
|
* |
|
337
|
|
|
* @throws ContainerExceptionInterface |
|
338
|
|
|
* @throws NotFoundExceptionInterface |
|
339
|
|
|
*/ |
|
340
|
4 |
|
protected static function normalizeIndexValueOnUpdate( |
|
341
|
|
|
array $routeParams, |
|
342
|
|
|
ContainerInterface $container, |
|
343
|
|
|
array $jsonData |
|
344
|
|
|
): array { |
|
345
|
|
|
// check that index in data and URL are identical |
|
346
|
4 |
|
$index = $routeParams[static::ROUTE_KEY_INDEX]; |
|
347
|
4 |
|
$dataSection = null; |
|
348
|
|
|
$hasIndexValue = |
|
349
|
4 |
|
array_key_exists(DI::KEYWORD_DATA, $jsonData) && |
|
350
|
4 |
|
array_key_exists(DI::KEYWORD_ID, ($dataSection = $jsonData[DI::KEYWORD_DATA])); |
|
351
|
4 |
|
if ($hasIndexValue === true) { |
|
352
|
3 |
|
assert($dataSection !== null); |
|
353
|
3 |
|
if ($dataSection[DI::KEYWORD_ID] !== $index) { |
|
354
|
|
|
/** @var FactoryInterface $factory */ |
|
355
|
1 |
|
$factory = $container->get(FactoryInterface::class); |
|
356
|
1 |
|
$errors = $factory->createErrorCollection(); |
|
357
|
1 |
|
$errors->addDataIdError( |
|
358
|
1 |
|
static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT) |
|
359
|
|
|
); |
|
360
|
|
|
|
|
361
|
3 |
|
throw new JsonApiException($errors); |
|
362
|
|
|
} |
|
363
|
|
|
} else { |
|
364
|
|
|
// put the index to data for our convenience |
|
365
|
1 |
|
$jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] = $index; |
|
366
|
|
|
} |
|
367
|
|
|
|
|
368
|
3 |
|
return $jsonData; |
|
369
|
|
|
} |
|
370
|
|
|
|
|
371
|
|
|
/** |
|
372
|
|
|
* @param int|string $parentIndex |
|
373
|
|
|
* @param string $relationshipName |
|
374
|
|
|
* @param int|string $childIndex |
|
375
|
|
|
* @param string $childApiClass |
|
376
|
|
|
* @param ContainerInterface $container |
|
377
|
|
|
* @param ServerRequestInterface $request |
|
378
|
|
|
* |
|
379
|
|
|
* @return ResponseInterface |
|
380
|
|
|
* |
|
381
|
|
|
* @throws ContainerExceptionInterface |
|
382
|
|
|
* @throws NotFoundExceptionInterface |
|
383
|
|
|
*/ |
|
384
|
1 |
|
protected static function deleteInRelationship( |
|
385
|
|
|
$parentIndex, |
|
386
|
|
|
string $relationshipName, |
|
387
|
|
|
$childIndex, |
|
388
|
|
|
string $childApiClass, |
|
389
|
|
|
ContainerInterface $container, |
|
390
|
|
|
ServerRequestInterface $request |
|
391
|
|
|
): ResponseInterface { |
|
392
|
|
|
/** @var SchemaInterface $schemaClass */ |
|
393
|
1 |
|
$schemaClass = static::SCHEMA_CLASS; |
|
394
|
1 |
|
$modelRelName = $schemaClass::getRelationshipMapping($relationshipName); |
|
395
|
1 |
|
$hasChild = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex); |
|
396
|
1 |
|
if ($hasChild === false) { |
|
397
|
1 |
|
return static::createResponses($container, $request)->getCodeResponse(404); |
|
398
|
|
|
} |
|
399
|
|
|
|
|
400
|
1 |
|
$childApi = static::createApi($container, $childApiClass); |
|
401
|
|
|
|
|
402
|
1 |
|
$childApi->remove($childIndex); |
|
403
|
1 |
|
$response = static::createResponses($container, $request)->getCodeResponse(204); |
|
404
|
|
|
|
|
405
|
1 |
|
return $response; |
|
406
|
|
|
} |
|
407
|
|
|
|
|
408
|
|
|
/** @noinspection PhpTooManyParametersInspection |
|
409
|
|
|
* @param int|string $parentIndex |
|
410
|
|
|
* @param string $relationshipName |
|
411
|
|
|
* @param int|string $childIndex |
|
412
|
|
|
* @param array $attributes |
|
413
|
|
|
* @param array $toMany |
|
414
|
|
|
* @param string $childApiClass |
|
415
|
|
|
* @param ContainerInterface $container |
|
416
|
|
|
* @param ServerRequestInterface $request |
|
417
|
|
|
* |
|
418
|
|
|
* @return ResponseInterface |
|
419
|
|
|
* |
|
420
|
|
|
* @throws ContainerExceptionInterface |
|
421
|
|
|
* @throws NotFoundExceptionInterface |
|
422
|
|
|
*/ |
|
423
|
2 |
View Code Duplication |
protected static function updateInRelationship( |
|
|
|
|
|
|
424
|
|
|
$parentIndex, |
|
425
|
|
|
string $relationshipName, |
|
426
|
|
|
$childIndex, |
|
427
|
|
|
array $attributes, |
|
428
|
|
|
array $toMany, |
|
429
|
|
|
string $childApiClass, |
|
430
|
|
|
ContainerInterface $container, |
|
431
|
|
|
ServerRequestInterface $request |
|
432
|
|
|
): ResponseInterface { |
|
433
|
|
|
/** @var CrudInterface $childApi */ |
|
434
|
2 |
|
list ($updated, $childApi) = static::updateInRelationshipImpl( |
|
|
|
|
|
|
435
|
2 |
|
$parentIndex, |
|
436
|
2 |
|
$relationshipName, |
|
437
|
2 |
|
$childIndex, |
|
438
|
2 |
|
$attributes, |
|
439
|
2 |
|
$toMany, |
|
440
|
2 |
|
$childApiClass, |
|
441
|
2 |
|
$container |
|
442
|
|
|
); |
|
443
|
|
|
|
|
444
|
2 |
|
$responses = static::createResponses($container, $request); |
|
445
|
2 |
|
if ($updated > 0) { |
|
446
|
1 |
|
$modelData = $childApi->read($childIndex); |
|
447
|
1 |
|
assert(!($modelData instanceof PaginatedDataInterface)); |
|
448
|
|
|
|
|
449
|
1 |
|
return $responses->getContentResponse($modelData); |
|
450
|
|
|
} |
|
451
|
|
|
|
|
452
|
1 |
|
return $responses->getCodeResponse(404); |
|
453
|
|
|
} |
|
454
|
|
|
|
|
455
|
|
|
/** |
|
456
|
|
|
* @param ContainerInterface $container |
|
457
|
|
|
* |
|
458
|
|
|
* @return JsonApiValidatorInterface |
|
459
|
|
|
* |
|
460
|
|
|
* @throws ContainerExceptionInterface |
|
461
|
|
|
* @throws NotFoundExceptionInterface |
|
462
|
|
|
*/ |
|
463
|
1 |
View Code Duplication |
protected static function createOnCreateValidator(ContainerInterface $container): JsonApiValidatorInterface |
|
|
|
|
|
|
464
|
|
|
{ |
|
465
|
1 |
|
assert( |
|
466
|
1 |
|
empty(static::ON_CREATE_VALIDATION_RULES_SET_CLASS) === false, |
|
467
|
1 |
|
'Validation rules set should be defined for class ' . static::class . '.' |
|
468
|
|
|
); |
|
469
|
|
|
|
|
470
|
1 |
|
return static::createJsonApiValidator($container, static::ON_CREATE_VALIDATION_RULES_SET_CLASS); |
|
471
|
|
|
} |
|
472
|
|
|
|
|
473
|
|
|
/** |
|
474
|
|
|
* @param ContainerInterface $container |
|
475
|
|
|
* |
|
476
|
|
|
* @return JsonApiValidatorInterface |
|
477
|
|
|
* |
|
478
|
|
|
* @throws ContainerExceptionInterface |
|
479
|
|
|
* @throws NotFoundExceptionInterface |
|
480
|
|
|
*/ |
|
481
|
3 |
View Code Duplication |
protected static function createOnUpdateValidator(ContainerInterface $container): JsonApiValidatorInterface |
|
|
|
|
|
|
482
|
|
|
{ |
|
483
|
3 |
|
assert( |
|
484
|
3 |
|
empty(static::ON_UPDATE_VALIDATION_RULES_SET_CLASS) === false, |
|
485
|
3 |
|
'Validation rules set should be defined for class ' . static::class . '.' |
|
486
|
|
|
); |
|
487
|
|
|
|
|
488
|
3 |
|
return static::createJsonApiValidator($container, static::ON_UPDATE_VALIDATION_RULES_SET_CLASS); |
|
489
|
|
|
} |
|
490
|
|
|
|
|
491
|
|
|
/** |
|
492
|
|
|
* @param ContainerInterface $container |
|
493
|
|
|
* @param string $rulesSetClass |
|
494
|
|
|
* |
|
495
|
|
|
* @return JsonApiValidatorInterface |
|
496
|
|
|
* |
|
497
|
|
|
* @throws ContainerExceptionInterface |
|
498
|
|
|
* @throws NotFoundExceptionInterface |
|
499
|
|
|
*/ |
|
500
|
6 |
|
protected static function createJsonApiValidator( |
|
501
|
|
|
ContainerInterface $container, |
|
502
|
|
|
string $rulesSetClass |
|
503
|
|
|
): JsonApiValidatorInterface { |
|
504
|
|
|
/** @var JsonApiValidatorFactoryInterface $validatorFactory */ |
|
505
|
6 |
|
$validatorFactory = $container->get(JsonApiValidatorFactoryInterface::class); |
|
506
|
6 |
|
$validator = $validatorFactory->createValidator($rulesSetClass); |
|
507
|
|
|
|
|
508
|
6 |
|
return $validator; |
|
509
|
|
|
} |
|
510
|
|
|
|
|
511
|
|
|
/** |
|
512
|
|
|
* @param ContainerInterface $container |
|
513
|
|
|
* |
|
514
|
|
|
* @return QueryParserInterface |
|
515
|
|
|
* |
|
516
|
|
|
* @throws ContainerExceptionInterface |
|
517
|
|
|
* @throws NotFoundExceptionInterface |
|
518
|
|
|
*/ |
|
519
|
16 |
|
protected static function createQueryParser(ContainerInterface $container): QueryParserInterface |
|
520
|
|
|
{ |
|
521
|
16 |
|
return $container->get(QueryParserInterface::class); |
|
522
|
|
|
} |
|
523
|
|
|
|
|
524
|
|
|
/** |
|
525
|
|
|
* @param QueryParserInterface $parser |
|
526
|
|
|
* |
|
527
|
|
|
* @return QueryParserInterface |
|
528
|
|
|
*/ |
|
529
|
12 |
|
protected static function configureOnIndexParser(QueryParserInterface $parser): QueryParserInterface |
|
530
|
|
|
{ |
|
531
|
12 |
|
return $parser; |
|
532
|
|
|
} |
|
533
|
|
|
|
|
534
|
|
|
/** |
|
535
|
|
|
* @param QueryParserInterface $parser |
|
536
|
|
|
* |
|
537
|
|
|
* @return QueryParserInterface |
|
538
|
|
|
*/ |
|
539
|
1 |
|
protected static function configureOnReadParser(QueryParserInterface $parser): QueryParserInterface |
|
540
|
|
|
{ |
|
541
|
1 |
|
return $parser; |
|
542
|
|
|
} |
|
543
|
|
|
|
|
544
|
|
|
/** |
|
545
|
|
|
* @param string $name |
|
546
|
|
|
* @param QueryParserInterface $parser |
|
547
|
|
|
* |
|
548
|
|
|
* @return QueryParserInterface |
|
549
|
|
|
* |
|
550
|
|
|
* @SuppressWarnings(PHPMD.UnusedFormalParameter) |
|
551
|
|
|
*/ |
|
552
|
2 |
|
protected static function configureOnReadRelationshipParser( |
|
553
|
|
|
/** @noinspection PhpUnusedParameterInspection */ string $name, |
|
|
|
|
|
|
554
|
|
|
QueryParserInterface $parser |
|
555
|
|
|
): QueryParserInterface { |
|
556
|
2 |
|
return $parser; |
|
557
|
|
|
} |
|
558
|
|
|
|
|
559
|
|
|
/** |
|
560
|
|
|
* @param string $name |
|
561
|
|
|
* @param QueryParserInterface $parser |
|
562
|
|
|
* |
|
563
|
|
|
* @return QueryParserInterface |
|
564
|
|
|
* |
|
565
|
|
|
* @SuppressWarnings(PHPMD.UnusedFormalParameter) |
|
566
|
|
|
*/ |
|
567
|
1 |
|
protected static function configureOnReadRelationshipIdentifiersParser( |
|
568
|
|
|
/** @noinspection PhpUnusedParameterInspection */ string $name, |
|
|
|
|
|
|
569
|
|
|
QueryParserInterface $parser |
|
570
|
|
|
): QueryParserInterface { |
|
571
|
1 |
|
return $parser; |
|
572
|
|
|
} |
|
573
|
|
|
|
|
574
|
|
|
/** |
|
575
|
|
|
* @param ContainerInterface $container |
|
576
|
|
|
* |
|
577
|
|
|
* @return ParametersMapperInterface |
|
578
|
|
|
* |
|
579
|
|
|
* @throws ContainerExceptionInterface |
|
580
|
|
|
* @throws NotFoundExceptionInterface |
|
581
|
|
|
*/ |
|
582
|
15 |
|
protected static function createParameterMapper(ContainerInterface $container): ParametersMapperInterface |
|
583
|
|
|
{ |
|
584
|
|
|
/** @var SchemaInterface $schemaClass */ |
|
585
|
15 |
|
$schemaClass = static::SCHEMA_CLASS; |
|
586
|
|
|
|
|
587
|
|
|
/** @var ParametersMapperInterface $mapper */ |
|
588
|
15 |
|
$mapper = $container->get(ParametersMapperInterface::class); |
|
589
|
15 |
|
$mapper->selectRootSchemeByResourceType($schemaClass::TYPE); |
|
590
|
|
|
|
|
591
|
15 |
|
return $mapper; |
|
592
|
|
|
} |
|
593
|
|
|
|
|
594
|
|
|
/** |
|
595
|
|
|
* @param ContainerInterface $container |
|
596
|
|
|
* @param array $captures |
|
597
|
|
|
* @param string $schemeClass |
|
598
|
|
|
* |
|
599
|
|
|
* @return array |
|
600
|
|
|
* |
|
601
|
|
|
* @throws ContainerExceptionInterface |
|
602
|
|
|
* @throws NotFoundExceptionInterface |
|
603
|
|
|
*/ |
|
604
|
5 |
|
protected static function mapSchemeDataToModelData( |
|
605
|
|
|
ContainerInterface $container, |
|
606
|
|
|
array $captures, |
|
607
|
|
|
string $schemeClass |
|
608
|
|
|
): array { |
|
609
|
5 |
|
assert(in_array(SchemaInterface::class, class_implements($schemeClass))); |
|
610
|
|
|
/** @var SchemaInterface $schemeClass */ |
|
611
|
|
|
|
|
612
|
5 |
|
$modelClass = $schemeClass::MODEL; |
|
613
|
5 |
|
assert(in_array(ModelInterface::class, class_implements($modelClass))); |
|
614
|
|
|
/** @var ModelInterface $modelClass */ |
|
615
|
|
|
|
|
616
|
|
|
/** @var ModelSchemeInfoInterface $schemeInfo */ |
|
617
|
5 |
|
$schemeInfo = $container->get(ModelSchemeInfoInterface::class); |
|
618
|
|
|
|
|
619
|
5 |
|
$index = null; |
|
620
|
5 |
|
$fields = []; |
|
621
|
5 |
|
$toManyIndexes = []; |
|
622
|
5 |
|
foreach ($captures as $name => $value) { |
|
623
|
5 |
|
if ($name === DI::KEYWORD_ID) { |
|
624
|
5 |
|
$index = $value; |
|
625
|
5 |
|
} elseif ($schemeClass::hasAttributeMapping($name) === true) { |
|
626
|
5 |
|
$fieldName = $schemeClass::getAttributeMapping($name); |
|
627
|
5 |
|
$fields[$fieldName] = $value; |
|
628
|
5 |
|
} elseif ($schemeClass::hasRelationshipMapping($name) === true) { |
|
629
|
2 |
|
$modelRelName = $schemeClass::getRelationshipMapping($name); |
|
630
|
2 |
|
$relType = $schemeInfo->getRelationshipType($modelClass, $modelRelName); |
|
631
|
2 |
|
if ($relType === RelationshipTypes::BELONGS_TO) { |
|
632
|
2 |
|
$fkName = $schemeInfo->getForeignKey($modelClass, $modelRelName); |
|
633
|
2 |
|
$fields[$fkName] = $value; |
|
634
|
2 |
|
} elseif ($relType === RelationshipTypes::BELONGS_TO_MANY) { |
|
635
|
5 |
|
$toManyIndexes[$modelRelName] = $value; |
|
636
|
|
|
} |
|
637
|
|
|
} |
|
638
|
|
|
} |
|
639
|
|
|
|
|
640
|
5 |
|
$result = [$index, $fields, $toManyIndexes]; |
|
641
|
|
|
|
|
642
|
5 |
|
return $result; |
|
643
|
|
|
} |
|
644
|
|
|
|
|
645
|
|
|
/** |
|
646
|
|
|
* @param ContainerInterface $container |
|
647
|
|
|
* @param ServerRequestInterface $request |
|
648
|
|
|
* |
|
649
|
|
|
* @return array |
|
650
|
|
|
* |
|
651
|
|
|
* @throws ContainerExceptionInterface |
|
652
|
|
|
* @throws NotFoundExceptionInterface |
|
653
|
|
|
*/ |
|
654
|
1 |
|
protected static function createImpl( |
|
655
|
|
|
ContainerInterface $container, |
|
656
|
|
|
ServerRequestInterface $request |
|
657
|
|
|
): array { |
|
658
|
1 |
|
$jsonData = static::readJsonFromRequest($container, $request); |
|
659
|
1 |
|
$validator = static::createOnCreateValidator($container); |
|
660
|
1 |
|
$captures = $validator->assert($jsonData)->getJsonApiCaptures(); |
|
661
|
|
|
|
|
662
|
|
|
list ($index, $attributes, $toMany) = |
|
663
|
1 |
|
static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS); |
|
664
|
|
|
|
|
665
|
1 |
|
$api = static::createApi($container); |
|
666
|
1 |
|
$index = $api->create($index, $attributes, $toMany); |
|
667
|
|
|
|
|
668
|
1 |
|
return [$index, $api]; |
|
669
|
|
|
} |
|
670
|
|
|
|
|
671
|
|
|
/** |
|
672
|
|
|
* @param array $routeParams |
|
673
|
|
|
* @param ContainerInterface $container |
|
674
|
|
|
* @param ServerRequestInterface $request |
|
675
|
|
|
* |
|
676
|
|
|
* @return array [int $updated, string $index, CrudInterface $api] |
|
677
|
|
|
* |
|
678
|
|
|
* @throws ContainerExceptionInterface |
|
679
|
|
|
* @throws NotFoundExceptionInterface |
|
680
|
|
|
*/ |
|
681
|
5 |
|
protected static function updateImpl( |
|
682
|
|
|
array $routeParams, |
|
683
|
|
|
ContainerInterface $container, |
|
684
|
|
|
ServerRequestInterface $request |
|
685
|
|
|
): array { |
|
686
|
5 |
|
$jsonData = static::normalizeIndexValueOnUpdate( |
|
687
|
5 |
|
$routeParams, |
|
688
|
5 |
|
$container, |
|
689
|
5 |
|
static::readJsonFromRequest($container, $request) |
|
690
|
|
|
); |
|
691
|
3 |
|
$validator = static::createOnUpdateValidator($container); |
|
692
|
3 |
|
$captures = $validator->assert($jsonData)->getJsonApiCaptures(); |
|
693
|
|
|
|
|
694
|
|
|
list ($index, $attributes, $toMany) = |
|
695
|
2 |
|
static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS); |
|
696
|
2 |
|
$api = static::createApi($container); |
|
697
|
|
|
|
|
698
|
2 |
|
$updated = $api->update($index, $attributes, $toMany); |
|
699
|
|
|
|
|
700
|
2 |
|
return [$updated, $index, $api]; |
|
701
|
|
|
} |
|
702
|
|
|
|
|
703
|
|
|
/** |
|
704
|
|
|
* @param $parentIndex |
|
705
|
|
|
* @param string $relationshipName |
|
706
|
|
|
* @param $childIndex |
|
707
|
|
|
* @param array $attributes |
|
708
|
|
|
* @param array $toMany |
|
709
|
|
|
* @param string $childApiClass |
|
710
|
|
|
* @param ContainerInterface $container |
|
711
|
|
|
* |
|
712
|
|
|
* @return array |
|
713
|
|
|
* |
|
714
|
|
|
* @throws ContainerExceptionInterface |
|
715
|
|
|
* @throws NotFoundExceptionInterface |
|
716
|
|
|
*/ |
|
717
|
2 |
|
private static function updateInRelationshipImpl( |
|
718
|
|
|
$parentIndex, |
|
719
|
|
|
string $relationshipName, |
|
720
|
|
|
$childIndex, |
|
721
|
|
|
array $attributes, |
|
722
|
|
|
array $toMany, |
|
723
|
|
|
string $childApiClass, |
|
724
|
|
|
ContainerInterface $container |
|
725
|
|
|
): array { |
|
726
|
|
|
/** @var SchemaInterface $schemaClass */ |
|
727
|
2 |
|
$schemaClass = static::SCHEMA_CLASS; |
|
728
|
2 |
|
$modelRelName = $schemaClass::getRelationshipMapping($relationshipName); |
|
729
|
2 |
|
$hasChild = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex); |
|
730
|
2 |
|
if ($hasChild === false) { |
|
731
|
1 |
|
return [0, null]; |
|
732
|
|
|
} |
|
733
|
|
|
|
|
734
|
1 |
|
$childApi = static::createApi($container, $childApiClass); |
|
735
|
|
|
|
|
736
|
1 |
|
$updated = $childApi->update($childIndex, $attributes, $toMany); |
|
|
|
|
|
|
737
|
|
|
|
|
738
|
1 |
|
return [$updated, $childApi]; |
|
739
|
|
|
} |
|
740
|
|
|
|
|
741
|
|
|
/** |
|
742
|
|
|
* @param ContainerInterface $container |
|
743
|
|
|
* @param string $namespace |
|
744
|
|
|
* |
|
745
|
|
|
* @return FormatterInterface |
|
746
|
|
|
* |
|
747
|
|
|
* @throws ContainerExceptionInterface |
|
748
|
|
|
* @throws NotFoundExceptionInterface |
|
749
|
|
|
*/ |
|
750
|
2 |
|
protected static function createMessageFormatter( |
|
751
|
|
|
ContainerInterface $container, |
|
752
|
|
|
string $namespace = Messages::RESOURCES_NAMESPACE |
|
753
|
|
|
): FormatterInterface { |
|
754
|
|
|
/** @var FormatterFactoryInterface $factory */ |
|
755
|
2 |
|
$factory = $container->get(FormatterFactoryInterface::class); |
|
756
|
2 |
|
$messageFormatter = $factory->createFormatter($namespace); |
|
757
|
|
|
|
|
758
|
2 |
|
return $messageFormatter; |
|
759
|
|
|
} |
|
760
|
|
|
} |
|
761
|
|
|
|
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.