Completed
Push — master ( 6173d9...210649 )
by Neomerx
04:26
created

defaultUpdateResponse()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 20
ccs 8
cts 8
cp 1
rs 9.6
c 0
b 0
f 0
cc 3
nc 2
nop 7
crap 3
1
<?php namespace Limoncello\Flute\Http\Traits;
2
3
/**
4
 * Copyright 2015-2018 [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\ModelSchemaInfoInterface;
22
use Limoncello\Contracts\Data\RelationshipTypes;
23
use Limoncello\Contracts\L10n\FormatterFactoryInterface;
24
use Limoncello\Contracts\Settings\SettingsProviderInterface;
25
use Limoncello\Flute\Contracts\Api\CrudInterface;
26
use Limoncello\Flute\Contracts\Encoder\EncoderInterface;
27
use Limoncello\Flute\Contracts\FactoryInterface;
28
use Limoncello\Flute\Contracts\Http\Query\ParametersMapperInterface;
29
use Limoncello\Flute\Contracts\Models\PaginatedDataInterface;
30
use Limoncello\Flute\Contracts\Schema\JsonSchemasInterface;
31
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
32
use Limoncello\Flute\Contracts\Validation\FormRulesInterface;
33
use Limoncello\Flute\Contracts\Validation\FormValidatorFactoryInterface;
34
use Limoncello\Flute\Contracts\Validation\FormValidatorInterface;
35
use Limoncello\Flute\Contracts\Validation\JsonApiDataRulesInterface;
36
use Limoncello\Flute\Contracts\Validation\JsonApiDataValidatingParserInterface;
37
use Limoncello\Flute\Contracts\Validation\JsonApiParserFactoryInterface;
38
use Limoncello\Flute\Contracts\Validation\JsonApiQueryRulesInterface;
39
use Limoncello\Flute\Contracts\Validation\JsonApiQueryValidatingParserInterface;
40
use Limoncello\Flute\Http\Responses;
41
use Limoncello\Flute\Package\FluteSettings as S;
42
use Limoncello\Flute\Resources\Messages\En\Generic;
43
use Limoncello\Flute\Validation\JsonApi\Rules\DefaultQueryValidationRules;
44
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as DI;
45
use Neomerx\JsonApi\Contracts\Encoder\Parameters\EncodingParametersInterface;
46
use Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface;
47
use Neomerx\JsonApi\Contracts\Http\ResponsesInterface;
48
use Neomerx\JsonApi\Encoder\Parameters\EncodingParameters;
49
use Neomerx\JsonApi\Exceptions\JsonApiException;
50
use Neomerx\JsonApi\Http\Headers\MediaType;
51
use Psr\Container\ContainerExceptionInterface;
52
use Psr\Container\ContainerInterface;
53
use Psr\Container\NotFoundExceptionInterface;
54
use Psr\Http\Message\ResponseInterface;
55
use Psr\Http\Message\UriInterface;
56
57
/**
58
 * @package Limoncello\Flute
59
 */
60
trait DefaultControllerMethodsTrait
61
{
62
    /** @noinspection PhpTooManyParametersInspection
63
     * @param array                                 $queryParams
64
     * @param UriInterface                          $requestUri
65
     * @param JsonApiQueryValidatingParserInterface $queryParser
66
     * @param ParametersMapperInterface             $mapper
67
     * @param CrudInterface                         $crud
68
     * @param SettingsProviderInterface             $provider
69
     * @param JsonSchemasInterface                  $jsonSchemas
70
     * @param EncoderInterface                      $encoder
71
     *
72
     * @return ResponseInterface
73
     */
74 12
    protected static function defaultIndexHandler(
75
        array $queryParams,
76
        UriInterface $requestUri,
77
        JsonApiQueryValidatingParserInterface $queryParser,
78
        ParametersMapperInterface $mapper,
79
        CrudInterface $crud,
80
        SettingsProviderInterface $provider,
81
        JsonSchemasInterface $jsonSchemas,
82
        EncoderInterface $encoder
83
    ): ResponseInterface {
84 12
        $queryParser->parse(null, $queryParams);
85
86 11
        $models = $mapper->applyQueryParameters($queryParser, $crud)->index();
87
88 10
        $encParams = self::defaultCreateEncodingParameters($queryParser);
89 10
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
90 10
        $response  = ($models->getData()) === null ?
91 10
            $responses->getCodeResponse(404) : $responses->getContentResponse($models);
92
93 10
        return $response;
94
    }
95
96
    /** @noinspection PhpTooManyParametersInspection
97
     * @param string                                $index
98
     * @param array                                 $queryParams
99
     * @param UriInterface                          $requestUri
100
     * @param JsonApiQueryValidatingParserInterface $queryParser
101
     * @param ParametersMapperInterface             $mapper
102
     * @param CrudInterface                         $crud
103
     * @param SettingsProviderInterface             $provider
104
     * @param JsonSchemasInterface                  $jsonSchemas
105
     * @param EncoderInterface                      $encoder
106
     *
107
     * @return ResponseInterface
108
     */
109 1
    protected static function defaultReadHandler(
110
        string $index,
111
        array $queryParams,
112
        UriInterface $requestUri,
113
        JsonApiQueryValidatingParserInterface $queryParser,
114
        ParametersMapperInterface $mapper,
115
        CrudInterface $crud,
116
        SettingsProviderInterface $provider,
117
        JsonSchemasInterface $jsonSchemas,
118
        EncoderInterface $encoder
119
    ): ResponseInterface {
120 1
        $queryParser->parse($index, $queryParams);
121 1
        $validatedIndex = $queryParser->getIdentity();
122
123 1
        $model = $mapper->applyQueryParameters($queryParser, $crud)->read($validatedIndex);
124 1
        assert(!($model instanceof PaginatedDataInterface));
125
126 1
        $encParams = self::defaultCreateEncodingParameters($queryParser);
127 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
128 1
        $response  = $model === null ?
129 1
            $responses->getCodeResponse(404) : $responses->getContentResponse($model);
130
131 1
        return $response;
132
    }
133
134
    /** @noinspection PhpTooManyParametersInspection
135
     * @param string                                $index
136
     * @param Closure                               $apiHandler
137
     * @param array                                 $queryParams
138
     * @param UriInterface                          $requestUri
139
     * @param JsonApiQueryValidatingParserInterface $queryParser
140
     * @param ParametersMapperInterface             $mapper
141
     * @param CrudInterface                         $crud
142
     * @param SettingsProviderInterface             $provider
143
     * @param JsonSchemasInterface                  $jsonSchemas
144
     * @param EncoderInterface                      $encoder
145
     *
146
     * @return ResponseInterface
147
     *
148
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
149
     */
150 2
    protected static function defaultReadRelationshipWithClosureHandler(
151
        string $index,
152
        Closure $apiHandler,
153
        array $queryParams,
154
        UriInterface $requestUri,
155
        JsonApiQueryValidatingParserInterface $queryParser,
156
        ParametersMapperInterface $mapper,
157
        CrudInterface $crud,
158
        SettingsProviderInterface $provider,
159
        JsonSchemasInterface $jsonSchemas,
160
        EncoderInterface $encoder
161
    ): ResponseInterface {
162 2
        $queryParser->parse($index, $queryParams);
163 2
        $mapper->applyQueryParameters($queryParser, $crud);
164
165 2
        $relData = call_user_func($apiHandler);
166
167 2
        $encParams = self::defaultCreateEncodingParameters($queryParser);
168 2
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
169
170 2
        $noData   = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null);
171 2
        $response = $noData === true ? $responses->getCodeResponse(404) : $responses->getContentResponse($relData);
172
173 2
        return $response;
174
    }
175
176
    /** @noinspection PhpTooManyParametersInspection
177
     * @param string                                $index
178
     * @param Closure                               $apiHandler
179
     * @param array                                 $queryParams
180
     * @param UriInterface                          $requestUri
181
     * @param JsonApiQueryValidatingParserInterface $queryParser
182
     * @param ParametersMapperInterface             $mapper
183
     * @param CrudInterface                         $crud
184
     * @param SettingsProviderInterface             $provider
185
     * @param JsonSchemasInterface                  $jsonSchemas
186
     * @param EncoderInterface                      $encoder
187
     *
188
     * @return ResponseInterface
189
     *
190
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
191
     */
192 1
    protected static function defaultReadRelationshipIdentifiersWithClosureHandler(
193
        string $index,
194
        Closure $apiHandler,
195
        array $queryParams,
196
        UriInterface $requestUri,
197
        JsonApiQueryValidatingParserInterface $queryParser,
198
        ParametersMapperInterface $mapper,
199
        CrudInterface $crud,
200
        SettingsProviderInterface $provider,
201
        JsonSchemasInterface $jsonSchemas,
202
        EncoderInterface $encoder
203
    ): ResponseInterface {
204 1
        $queryParser->parse($index, $queryParams);
205 1
        $mapper->applyQueryParameters($queryParser, $crud);
206
207 1
        $relData = call_user_func($apiHandler);
208
209 1
        $encParams = self::defaultCreateEncodingParameters($queryParser);
210 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
211
212 1
        $noData   = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null);
213 1
        $response = $noData === true ? $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData);
214
215 1
        return $response;
216
    }
217
218
    /** @noinspection PhpTooManyParametersInspection
219
     * @param UriInterface                         $requestUri
220
     * @param string                               $requestBody
221
     * @param string                               $schemaClass
222
     * @param ModelSchemaInfoInterface             $schemaInfo
223
     * @param JsonApiDataValidatingParserInterface $validator
224
     * @param CrudInterface                        $crud
225
     * @param SettingsProviderInterface            $provider
226
     * @param JsonSchemasInterface                 $jsonSchemas
227
     * @param EncoderInterface                     $encoder
228
     * @param FactoryInterface                     $errorFactory
229
     * @param FormatterFactoryInterface            $formatterFactory
230
     *
231
     * @return ResponseInterface
232
     *
233
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
234
     */
235 1
    protected static function defaultCreateHandler(
236
        UriInterface $requestUri,
237
        string $requestBody,
238
        string $schemaClass,
239
        ModelSchemaInfoInterface $schemaInfo,
240
        JsonApiDataValidatingParserInterface $validator,
241
        CrudInterface $crud,
242
        SettingsProviderInterface $provider,
243
        JsonSchemasInterface $jsonSchemas,
244
        EncoderInterface $encoder,
245
        FactoryInterface $errorFactory,
246
        FormatterFactoryInterface $formatterFactory
247
    ): ResponseInterface {
248
        // some of the users want to reuse default `create` but have a custom part for responses
249
        // to meet this requirement it is split into two parts.
250 1
        $index = static::defaultCreate(
251 1
            $requestBody,
252 1
            $schemaClass,
253 1
            $schemaInfo,
254 1
            $validator,
255 1
            $crud,
256 1
            $errorFactory,
257 1
            $formatterFactory
258
        );
259
260 1
        return static::defaultCreateResponse($index, $requestUri, $crud, $provider, $jsonSchemas, $encoder);
261
    }
262
263
    /**
264
     * @param string                               $requestBody
265
     * @param string                               $schemaClass
266
     * @param ModelSchemaInfoInterface             $schemaInfo
267
     * @param JsonApiDataValidatingParserInterface $validator
268
     * @param CrudInterface                        $crud
269
     * @param FactoryInterface                     $errorFactory
270
     * @param FormatterFactoryInterface            $formatterFactory
271
     *
272
     * @return ResponseInterface
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
273
     */
274 1
    protected static function defaultCreate(
275
        string $requestBody,
276
        string $schemaClass,
277
        ModelSchemaInfoInterface $schemaInfo,
278
        JsonApiDataValidatingParserInterface $validator,
279
        CrudInterface $crud,
280
        FactoryInterface $errorFactory,
281
        FormatterFactoryInterface $formatterFactory
282
    ): string {
283 1
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
284 1
        $captures = $validator->assert($jsonData)->getJsonApiCaptures();
285
286 1
        list ($index, $attributes, $toMany) = static::mapSchemaDataToModelData($captures, $schemaClass, $schemaInfo);
287
288 1
        $index = $crud->create($index, $attributes, $toMany);
289
290 1
        return $index;
291
    }
292
293
    /**
294
     * @param string                    $index
295
     * @param UriInterface              $requestUri
296
     * @param CrudInterface             $crud
297
     * @param SettingsProviderInterface $provider
298
     * @param JsonSchemasInterface      $jsonSchemas
299
     * @param EncoderInterface          $encoder
300
     *
301
     * @return ResponseInterface
302
     */
303 1
    protected static function defaultCreateResponse(
304
        string $index,
305
        UriInterface $requestUri,
306
        CrudInterface $crud,
307
        SettingsProviderInterface $provider,
308
        JsonSchemasInterface $jsonSchemas,
309
        EncoderInterface $encoder
310
    ): ResponseInterface {
311 1
        $model = $crud->read($index);
312 1
        assert($model !== null && !($model instanceof PaginatedDataInterface));
313
314 1
        $encParams = null;
315 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
316 1
        $response  = $responses->getCreatedResponse($model);
317
318 1
        return $response;
319
    }
320
321
    /** @noinspection PhpTooManyParametersInspection
322
     * @param string                               $index
323
     * @param UriInterface                         $requestUri
324
     * @param string                               $requestBody
325
     * @param string                               $schemaClass
326
     * @param ModelSchemaInfoInterface             $schemaInfo
327
     * @param JsonApiDataValidatingParserInterface $validator
328
     * @param CrudInterface                        $crud
329
     * @param SettingsProviderInterface            $provider
330
     * @param JsonSchemasInterface                 $jsonSchemas
331
     * @param EncoderInterface                     $encoder
332
     * @param FactoryInterface                     $errorFactory
333
     * @param FormatterFactoryInterface            $formatterFactory
334
     * @param string                               $messagesNamespace
335
     * @param string                               $errorMessage
336
     *
337
     * @return ResponseInterface
338
     *
339
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
340
     */
341 5
    protected static function defaultUpdateHandler(
342
        string $index,
343
        UriInterface $requestUri,
344
        string $requestBody,
345
        string $schemaClass,
346
        ModelSchemaInfoInterface $schemaInfo,
347
        JsonApiDataValidatingParserInterface $validator,
348
        CrudInterface $crud,
349
        SettingsProviderInterface $provider,
350
        JsonSchemasInterface $jsonSchemas,
351
        EncoderInterface $encoder,
352
        FactoryInterface $errorFactory,
353
        FormatterFactoryInterface $formatterFactory,
354
        string $messagesNamespace = S::GENERIC_NAMESPACE,
355
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
356
    ): ResponseInterface {
357
        // some of the users want to reuse default `update` but have a custom part for responses
358
        // to meet this requirement it is split into two parts.
359 5
        $updated = static::defaultUpdate(
360 5
            $index,
361 5
            $requestBody,
362 5
            $schemaClass,
363 5
            $schemaInfo,
364 5
            $validator,
365 5
            $crud,
366 5
            $errorFactory,
367 5
            $formatterFactory,
368 5
            $messagesNamespace,
369 5
            $errorMessage
370
        );
371
372 2
        return static::defaultUpdateResponse($updated, $index, $requestUri, $crud, $provider, $jsonSchemas, $encoder);
373
    }
374
375
    /** @noinspection PhpTooManyParametersInspection
376
     * @param string                               $index
377
     * @param string                               $requestBody
378
     * @param string                               $schemaClass
379
     * @param ModelSchemaInfoInterface             $schemaInfo
380
     * @param JsonApiDataValidatingParserInterface $validator
381
     * @param CrudInterface                        $crud
382
     * @param FactoryInterface                     $errorFactory
383
     * @param FormatterFactoryInterface            $formatterFactory
384
     * @param string                               $messagesNamespace
385
     * @param string                               $errorMessage
386
     *
387
     * @return int
388
     *
389
     * @SuppressWarnings(PHPMD.ElseExpression)
390
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
391
     */
392 5
    protected static function defaultUpdate(
393
        string $index,
394
        string $requestBody,
395
        string $schemaClass,
396
        ModelSchemaInfoInterface $schemaInfo,
397
        JsonApiDataValidatingParserInterface $validator,
398
        CrudInterface $crud,
399
        FactoryInterface $errorFactory,
400
        FormatterFactoryInterface $formatterFactory,
401
        string $messagesNamespace = S::GENERIC_NAMESPACE,
402
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
403
    ): int {
404 5
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
405
        // check that index in data and URL are identical
406 4
        $indexValue = $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] ?? null;
407 4
        if (empty($indexValue) === false) {
408 3
            if ($indexValue !== $index) {
409 1
                $errors    = $errorFactory->createErrorCollection();
410 1
                $formatter = $formatterFactory->createFormatter($messagesNamespace);
411 1
                $errors->addDataIdError($formatter->formatMessage($errorMessage));
412
413 3
                throw new JsonApiException($errors);
414
            }
415
        } else {
416
            // put the index to data for our convenience
417 1
            $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] = $index;
418
        }
419
        // validate the data
420 3
        $captures = $validator->assert($jsonData)->getJsonApiCaptures();
421
422 2
        list ($index, $attributes, $toMany) = static::mapSchemaDataToModelData($captures, $schemaClass, $schemaInfo);
423
424 2
        $updated = $crud->update($index, $attributes, $toMany);
425
426 2
        return $updated;
427
    }
428
429
    /**
430
     * @param int                       $updated
431
     * @param string                    $index
432
     * @param UriInterface              $requestUri
433
     * @param CrudInterface             $crud
434
     * @param SettingsProviderInterface $provider
435
     * @param JsonSchemasInterface      $jsonSchemas
436
     * @param EncoderInterface          $encoder
437
     *
438
     * @return ResponseInterface
439
     *
440
     * @SuppressWarnings(PHPMD.ElseExpression)
441
     */
442 3
    protected static function defaultUpdateResponse(
443
        int $updated,
444
        string $index,
445
        UriInterface $requestUri,
446
        CrudInterface $crud,
447
        SettingsProviderInterface $provider,
448
        JsonSchemasInterface $jsonSchemas,
449
        EncoderInterface $encoder
450
    ): ResponseInterface {
451 3
        $encParams = null;
452 3
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
453 3
        if ($updated > 0 && ($model = $crud->read($index)) !== null) {
454 2
            assert(!($model instanceof PaginatedDataInterface));
455 2
            $response = $responses->getContentResponse($model);
456
        } else {
457 1
            $response = $responses->getCodeResponse(404);
458
        }
459
460 3
        return $response;
461
    }
462
463
    /**
464
     * @param string                                $index
465
     * @param UriInterface                          $requestUri
466
     * @param JsonApiQueryValidatingParserInterface $queryParser
467
     * @param CrudInterface                         $crud
468
     * @param SettingsProviderInterface             $provider
469
     * @param JsonSchemasInterface                  $jsonSchemas
470
     * @param EncoderInterface                      $encoder
471
     *
472
     * @return ResponseInterface
473
     */
474 1
    protected static function defaultDeleteHandler(
475
        string $index,
476
        UriInterface $requestUri,
477
        JsonApiQueryValidatingParserInterface $queryParser,
478
        CrudInterface $crud,
479
        SettingsProviderInterface $provider,
480
        JsonSchemasInterface $jsonSchemas,
481
        EncoderInterface $encoder
482
    ): ResponseInterface {
483 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
484 1
        $crud->remove($validatedIndex);
485
486 1
        $encParams = null;
487 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
488 1
        $response  = $responses->getCodeResponse(204);
489
490 1
        return $response;
491
    }
492
493
    /** @noinspection PhpTooManyParametersInspection
494
     * @param string                                $index
495
     * @param string                                $jsonRelName
496
     * @param string                                $modelRelName
497
     * @param UriInterface                          $requestUri
498
     * @param string                                $requestBody
499
     * @param string                                $schemaClass
500
     * @param ModelSchemaInfoInterface              $schemaInfo
501
     * @param JsonApiQueryValidatingParserInterface $queryParser
502
     * @param JsonApiDataValidatingParserInterface  $dataValidator
503
     * @param CrudInterface                         $parentCrud
504
     * @param SettingsProviderInterface             $provider
505
     * @param JsonSchemasInterface                  $jsonSchemas
506
     * @param EncoderInterface                      $encoder
507
     * @param FactoryInterface                      $errorFactory
508
     * @param FormatterFactoryInterface             $formatterFactory
509
     *
510
     * @return ResponseInterface
511
     *
512
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
513
     */
514 1
    protected static function defaultAddInRelationshipHandler(
515
        string $index,
516
        string $jsonRelName,
517
        string $modelRelName,
518
        UriInterface $requestUri,
519
        string $requestBody,
520
        string $schemaClass,
521
        ModelSchemaInfoInterface $schemaInfo,
522
        JsonApiQueryValidatingParserInterface $queryParser,
523
        JsonApiDataValidatingParserInterface $dataValidator,
524
        CrudInterface $parentCrud,
525
        SettingsProviderInterface $provider,
526
        JsonSchemasInterface $jsonSchemas,
527
        EncoderInterface $encoder,
528
        FactoryInterface $errorFactory,
529
        FormatterFactoryInterface $formatterFactory
530
    ): ResponseInterface {
531
        /** @var SchemaInterface $schemaClass */
532 1
        assert(array_key_exists(SchemaInterface::class, class_implements($schemaClass)) === true);
533 1
        $modelClass = $schemaClass::MODEL;
534 1
        assert($schemaInfo->hasRelationship($modelClass, $modelRelName));
535 1
        assert($schemaInfo->getRelationshipType($modelClass, $modelRelName) === RelationshipTypes::BELONGS_TO_MANY);
536
537 1
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
538 1
        $captures = $dataValidator->assertRelationship($index, $jsonRelName, $jsonData)->getJsonApiCaptures();
539 1
        $relIds   = $captures[$jsonRelName];
540
541 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
542 1
        $parentCrud->createInBelongsToManyRelationship($validatedIndex, $modelRelName, $relIds);
543
544 1
        $encParams = null;
545 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
546 1
        $response  = $responses->getCodeResponse(204);
547
548 1
        return $response;
549
    }
550
551
    /** @noinspection PhpTooManyParametersInspection
552
     * @param string                                $index
553
     * @param string                                $jsonRelName
554
     * @param string                                $modelRelName
555
     * @param UriInterface                          $requestUri
556
     * @param string                                $requestBody
557
     * @param string                                $schemaClass
558
     * @param ModelSchemaInfoInterface              $schemaInfo
559
     * @param JsonApiQueryValidatingParserInterface $queryParser
560
     * @param JsonApiDataValidatingParserInterface  $dataValidator
561
     * @param CrudInterface                         $parentCrud
562
     * @param SettingsProviderInterface             $provider
563
     * @param JsonSchemasInterface                  $jsonSchemas
564
     * @param EncoderInterface                      $encoder
565
     * @param FactoryInterface                      $errorFactory
566
     * @param FormatterFactoryInterface             $formatterFactory
567
     *
568
     * @return ResponseInterface
569
     *
570
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
571
     */
572 1
    protected static function defaultDeleteInRelationshipHandler(
573
        string $index,
574
        string $jsonRelName,
575
        string $modelRelName,
576
        UriInterface $requestUri,
577
        string $requestBody,
578
        string $schemaClass,
579
        ModelSchemaInfoInterface $schemaInfo,
580
        JsonApiQueryValidatingParserInterface $queryParser,
581
        JsonApiDataValidatingParserInterface $dataValidator,
582
        CrudInterface $parentCrud,
583
        SettingsProviderInterface $provider,
584
        JsonSchemasInterface $jsonSchemas,
585
        EncoderInterface $encoder,
586
        FactoryInterface $errorFactory,
587
        FormatterFactoryInterface $formatterFactory
588
    ): ResponseInterface {
589
        /** @var SchemaInterface $schemaClass */
590 1
        assert(array_key_exists(SchemaInterface::class, class_implements($schemaClass)) === true);
591 1
        $modelClass = $schemaClass::MODEL;
592 1
        assert($schemaInfo->hasRelationship($modelClass, $modelRelName));
593 1
        assert($schemaInfo->getRelationshipType($modelClass, $modelRelName) === RelationshipTypes::BELONGS_TO_MANY);
594
595 1
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
596 1
        $captures = $dataValidator->assertRelationship($index, $jsonRelName, $jsonData)->getJsonApiCaptures();
597 1
        $relIds   = $captures[$jsonRelName];
598
599 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
600 1
        $parentCrud->removeInBelongsToManyRelationship($validatedIndex, $modelRelName, $relIds);
601
602 1
        $encParams = null;
603 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemas, $encoder, $encParams);
604 1
        $response  = $responses->getCodeResponse(204);
605
606 1
        return $response;
607
    }
608
609
    /** @noinspection PhpTooManyParametersInspection
610
     * @param string                                $index
611
     * @param string                                $jsonRelName
612
     * @param string                                $modelRelName
613
     * @param UriInterface                          $requestUri
614
     * @param string                                $requestBody
615
     * @param string                                $schemaClass
616
     * @param ModelSchemaInfoInterface              $schemaInfo
617
     * @param JsonApiQueryValidatingParserInterface $queryParser
618
     * @param JsonApiDataValidatingParserInterface  $dataValidator
619
     * @param CrudInterface                         $crud
620
     * @param SettingsProviderInterface             $provider
621
     * @param JsonSchemasInterface                  $jsonSchemas
622
     * @param EncoderInterface                      $encoder
623
     * @param FactoryInterface                      $errorFactory
624
     * @param FormatterFactoryInterface             $formatterFactory
625
     *
626
     * @return ResponseInterface
627
     *
628
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
629
     */
630 1
    protected static function defaultReplaceInRelationship(
631
        string $index,
632
        string $jsonRelName,
633
        string $modelRelName,
634
        UriInterface $requestUri,
635
        string $requestBody,
636
        string $schemaClass,
637
        ModelSchemaInfoInterface $schemaInfo,
638
        JsonApiQueryValidatingParserInterface $queryParser,
639
        JsonApiDataValidatingParserInterface $dataValidator,
640
        CrudInterface $crud,
641
        SettingsProviderInterface $provider,
642
        JsonSchemasInterface $jsonSchemas,
643
        EncoderInterface $encoder,
644
        FactoryInterface $errorFactory,
645
        FormatterFactoryInterface $formatterFactory
646
    ): ResponseInterface {
647
        /** @var SchemaInterface $schemaClass */
648 1
        assert(array_key_exists(SchemaInterface::class, class_implements($schemaClass)) === true);
649 1
        $modelClass = $schemaClass::MODEL;
650 1
        assert($schemaInfo->hasRelationship($modelClass, $modelRelName));
651 1
        assert(
652 1
            ($type =$schemaInfo->getRelationshipType($modelClass, $modelRelName)) === RelationshipTypes::BELONGS_TO ||
653 1
            $type === RelationshipTypes::BELONGS_TO_MANY
654
        );
655
656 1
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
657 1
        $captures = $dataValidator->assertRelationship($index, $jsonRelName, $jsonData)->getJsonApiCaptures();
658
659
        // If we are here then we have something in 'data' section.
660
661 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
662 1
        list (, $attributes, $toMany) = static::mapSchemaDataToModelData($captures, $schemaClass, $schemaInfo);
0 ignored issues
show
Documentation introduced by
$schemaClass is of type object<Limoncello\Flute\...Schema\SchemaInterface>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
663
664 1
        $updated = $crud->update($validatedIndex, $attributes, $toMany);
665
666 1
        return static::defaultUpdateResponse($updated, $index, $requestUri, $crud, $provider, $jsonSchemas, $encoder);
667
    }
668
669
    /**
670
     * @param ContainerInterface $container
671
     * @param string             $rulesClass
672
     *
673
     * @return JsonApiQueryValidatingParserInterface
674
     *
675
     * @throws ContainerExceptionInterface
676
     * @throws NotFoundExceptionInterface
677
     */
678 20
    protected static function defaultCreateQueryParser(
679
        ContainerInterface $container,
680
        string $rulesClass = DefaultQueryValidationRules::class
681
    ): JsonApiQueryValidatingParserInterface {
682 20
        static::assertClassImplements($rulesClass, JsonApiQueryRulesInterface::class);
683
684
        /** @var JsonApiParserFactoryInterface $factory */
685 20
        $factory   = $container->get(JsonApiParserFactoryInterface::class);
686 20
        $validator = $factory->createQueryParser($rulesClass);
687
688 20
        return $validator;
689
    }
690
691
    /**
692
     * @param ContainerInterface $container
693
     * @param string             $rulesClass
694
     *
695
     * @return JsonApiDataValidatingParserInterface
696
     *
697
     * @throws ContainerExceptionInterface
698
     * @throws NotFoundExceptionInterface
699
     */
700 9
    protected static function defaultCreateDataParser(
701
        ContainerInterface $container,
702
        string $rulesClass
703
    ): JsonApiDataValidatingParserInterface {
704 9
        static::assertClassImplements($rulesClass, JsonApiDataRulesInterface::class);
705
706
        /** @var JsonApiParserFactoryInterface $factory */
707 9
        $factory   = $container->get(JsonApiParserFactoryInterface::class);
708 9
        $validator = $factory->createDataParser($rulesClass);
709
710 9
        return $validator;
711
    }
712
713
    /**
714
     * @param ContainerInterface $container
715
     * @param string             $rulesClass
716
     *
717
     * @return FormValidatorInterface
718
     *
719
     * @throws ContainerExceptionInterface
720
     * @throws NotFoundExceptionInterface
721
     */
722 1
    protected static function defaultCreateFormValidator(
723
        ContainerInterface $container,
724
        string $rulesClass
725
    ): FormValidatorInterface {
726 1
        static::assertClassImplements($rulesClass, FormRulesInterface::class);
727
728
        /** @var FormValidatorFactoryInterface $factory */
729 1
        $factory   = $container->get(FormValidatorFactoryInterface::class);
730 1
        $validator = $factory->createValidator($rulesClass);
731
732 1
        return $validator;
733
    }
734
735
    /**
736
     * @param ContainerInterface $container
737
     * @param string             $schemaClass
738
     *
739
     * @return ParametersMapperInterface
740
     *
741
     * @throws ContainerExceptionInterface
742
     * @throws NotFoundExceptionInterface
743
     */
744 16
    protected static function defaultCreateParameterMapper(
745
        ContainerInterface $container,
746
        string $schemaClass
747
    ): ParametersMapperInterface {
748 16
        static::assertClassImplements($schemaClass, SchemaInterface::class);
749
750
        /** @var SchemaInterface $schemaClass */
751 16
        $jsonResourceType = $schemaClass::TYPE;
752
753
        /** @var ParametersMapperInterface $mapper */
754 16
        $mapper = $container->get(ParametersMapperInterface::class);
755 16
        $mapper->selectRootSchemaByResourceType($jsonResourceType);
756
757 16
        return $mapper;
758
    }
759
760
    /**
761
     * @param JsonApiQueryValidatingParserInterface $queryParser
762
     *
763
     * @return EncodingParametersInterface
764
     */
765 14
    protected static function defaultCreateEncodingParameters(
766
        JsonApiQueryValidatingParserInterface $queryParser
767
    ): EncodingParametersInterface {
768 14
        return new EncodingParameters(
769 14
            $queryParser->hasIncludes() === true ? array_keys($queryParser->getIncludes()) : null,
0 ignored issues
show
Bug introduced by
It seems like $queryParser->hasInclude...->getIncludes()) : null can also be of type array; however, Neomerx\JsonApi\Encoder\...rameters::__construct() does only seem to accept null|array<integer,string>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
770 14
            $queryParser->hasFields() === true ? $queryParser->getFields() : null
771
        );
772
    }
773
774
    /**
775
     * @param ContainerInterface $container
776
     * @param string|null        $class
777
     *
778
     * @return CrudInterface
779
     *
780
     * @throws ContainerExceptionInterface
781
     * @throws NotFoundExceptionInterface
782
     */
783 26
    protected static function defaultCreateApi(ContainerInterface $container, string $class): CrudInterface
784
    {
785 26
        static::assertClassImplements($class, CrudInterface::class);
786
787
        /** @var FactoryInterface $factory */
788 26
        $factory = $container->get(FactoryInterface::class);
789 26
        $api     = $factory->createApi($class);
790
791 26
        return $api;
792
    }
793
794
    /**
795
     * @param UriInterface                     $requestUri
796
     * @param SettingsProviderInterface        $provider
797
     * @param JsonSchemasInterface             $jsonSchemas
798
     * @param EncoderInterface                 $encoder
799
     * @param EncodingParametersInterface|null $parameters
800
     *
801
     * @return ResponsesInterface
802
     */
803 21
    protected static function defaultCreateResponses(
804
        UriInterface $requestUri,
805
        SettingsProviderInterface $provider,
806
        JsonSchemasInterface $jsonSchemas,
807
        EncoderInterface $encoder,
808
        ?EncodingParametersInterface $parameters
809
    ): ResponsesInterface {
810 21
        $encoder->forOriginalUri($requestUri);
811 21
        $settings  = $provider->get(S::class);
812 21
        $responses = new Responses(
813 21
            new MediaType(MediaTypeInterface::JSON_API_TYPE, MediaTypeInterface::JSON_API_SUB_TYPE),
814 21
            $encoder,
815 21
            $jsonSchemas,
816 21
            $parameters,
817 21
            $settings[S::KEY_URI_PREFIX],
818 21
            $settings[S::KEY_META]
819
        );
820
821 21
        return $responses;
822
    }
823
824
    /**
825
     * Developers can override the method in order to add/remove some data for `create`/`update` inputs.
826
     *
827
     * @param string                    $requestBody
828
     * @param FactoryInterface          $errorFactory
829
     * @param FormatterFactoryInterface $formatterFactory
830
     * @param string                    $messagesNamespace
831
     * @param string                    $errorMessage
832
     *
833
     * @return array
834
     */
835 9
    protected static function readJsonFromRequest(
836
        string $requestBody,
837
        FactoryInterface $errorFactory,
838
        FormatterFactoryInterface $formatterFactory,
839
        string $messagesNamespace = S::GENERIC_NAMESPACE,
840
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
841
    ): array {
842 9
        if (empty($requestBody) === true || ($json = json_decode($requestBody, true)) === null) {
843 1
            $formatter = $formatterFactory->createFormatter($messagesNamespace);
844 1
            $errors    = $errorFactory->createErrorCollection();
845 1
            $errors->addDataError($formatter->formatMessage($errorMessage));
846
847 1
            throw new JsonApiException($errors);
848
        }
849
850 8
        return $json;
851
    }
852
853
    /**
854
     * Developers can override the method in order to use custom data mapping from a Schema to Model.
855
     *
856
     * @param array                    $captures
857
     * @param string                   $schemaClass
858
     * @param ModelSchemaInfoInterface $schemaInfo
859
     *
860
     * @return array
861
     */
862 4
    protected static function mapSchemaDataToModelData(
863
        array $captures,
864
        string $schemaClass,
865
        ModelSchemaInfoInterface $schemaInfo
866
    ): array {
867 4
        static::assertClassImplements($schemaClass, SchemaInterface::class);
868
869
        /** @var SchemaInterface $schemaClass */
870 4
        static::assertClassImplements($modelClass = $schemaClass::MODEL, ModelInterface::class);
871
        /** @var ModelInterface $modelClass */
872
873 4
        $index         = null;
874 4
        $fields        = [];
875 4
        $toManyIndexes = [];
876 4
        foreach ($captures as $name => $value) {
877 4
            assert(is_string($name) === true);
878 4
            if ($name === DI::KEYWORD_ID) {
879 3
                $index = $value;
880 4
            } elseif ($schemaClass::hasAttributeMapping($name) === true) {
881 3
                $fieldName          = $schemaClass::getAttributeMapping($name);
882 3
                $fields[$fieldName] = $value;
883 4
            } elseif ($schemaClass::hasRelationshipMapping($name) === true) {
884 3
                $modelRelName = $schemaClass::getRelationshipMapping($name);
885 3
                $relType      = $schemaInfo->getRelationshipType($modelClass, $modelRelName);
0 ignored issues
show
Documentation introduced by
$modelClass is of type object<Limoncello\Contra...ication\ModelInterface>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
886 3
                if ($relType === RelationshipTypes::BELONGS_TO) {
887 3
                    $fkName          = $schemaInfo->getForeignKey($modelClass, $modelRelName);
0 ignored issues
show
Documentation introduced by
$modelClass is of type object<Limoncello\Contra...ication\ModelInterface>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
888 3
                    $fields[$fkName] = $value;
889 2
                } elseif ($relType === RelationshipTypes::BELONGS_TO_MANY) {
890 4
                    $toManyIndexes[$modelRelName] = $value;
891
                }
892
            }
893
        }
894
895 4
        $result = [$index, $fields, $toManyIndexes];
896
897 4
        return $result;
898
    }
899
900
    /**
901
     * @param null|string $value
902
     *
903
     * @return void
904
     */
905 26
    private static function assertClassValueDefined(?string $value): void
906
    {
907 26
        assert(empty($value) === false, 'Value should be defined in `' . static::class . '`.');
908
    }
909
910
    /**
911
     * @param string $class
912
     * @param string $interface
913
     *
914
     * @return void
915
     */
916 27
    private static function assertClassImplements(string $class, string $interface): void
917
    {
918 27
        assert(
919 27
            array_key_exists($interface, class_implements($class)) === true,
920 27
            "Class `$class` should implement `" . $interface . '` interface.'
921
        );
922
    }
923
}
924