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

defaultUpdateInRelationshipHandler()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 43

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 43
ccs 20
cts 20
cp 1
rs 9.232
c 0
b 0
f 0
cc 2
nc 2
nop 17
crap 2

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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