Completed
Push — develop ( de7f4b...5c2193 )
by Neomerx
09:05
created

DefaultControllerMethodsTrait   F

Complexity

Total Complexity 53

Size/Duplication

Total Lines 755
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 20

Test Coverage

Coverage 70.67%

Importance

Changes 0
Metric Value
wmc 53
lcom 1
cbo 20
dl 0
loc 755
ccs 147
cts 208
cp 0.7067
rs 1.7254
c 0
b 0
f 0

22 Methods

Rating   Name   Duplication   Size   Complexity  
B defaultIndexHandler() 0 24 4
B defaultReadHandler() 0 26 4
B defaultReadRelationshipWithClosureHandler() 0 27 6
B defaultReadRelationshipIdentifiersWithClosureHandler() 0 27 6
B defaultCreateHandler() 0 27 1
A defaultCreate() 0 18 1
A defaultCreateResponse() 0 17 2
B defaultUpdateHandler() 0 33 1
B defaultUpdate() 0 36 3
A defaultUpdateResponse() 0 21 3
B defaultUpdateInRelationshipHandler() 0 43 2
A defaultDeleteHandler() 0 16 1
B defaultDeleteInRelationshipHandler() 0 27 2
A defaultCreateQueryParser() 0 12 1
A defaultCreateDataParser() 0 12 1
A defaultCreateParameterMapper() 0 15 1
A defaultCreateApi() 0 10 1
A defaultCreateResponses() 0 20 1
C mapSchemeDataToModelData() 0 37 7
A readJsonFromRequest() 0 17 3
A assertClassValueDefined() 0 4 1
A assertClassImplements() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like DefaultControllerMethodsTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DefaultControllerMethodsTrait, and based on these observations, apply Extract Interface, too.

1
<?php namespace Limoncello\Flute\Http\Traits;
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\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\JsonSchemesInterface;
31
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
32
use Limoncello\Flute\Contracts\Validation\JsonApiDataRulesInterface;
33
use Limoncello\Flute\Contracts\Validation\JsonApiDataValidatingParserInterface;
34
use Limoncello\Flute\Contracts\Validation\JsonApiParserFactoryInterface;
35
use Limoncello\Flute\Contracts\Validation\JsonApiQueryRulesInterface;
36
use Limoncello\Flute\Contracts\Validation\JsonApiQueryValidatingParserInterface;
37
use Limoncello\Flute\Http\Responses;
38
use Limoncello\Flute\Package\FluteSettings as S;
39
use Limoncello\Flute\Resources\Messages\En\Generic;
40
use Limoncello\Flute\Validation\JsonApi\DefaultQueryValidationRules;
41
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as DI;
42
use Neomerx\JsonApi\Contracts\Encoder\Parameters\EncodingParametersInterface;
43
use Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface;
44
use Neomerx\JsonApi\Contracts\Http\ResponsesInterface;
45
use Neomerx\JsonApi\Encoder\Parameters\EncodingParameters;
46
use Neomerx\JsonApi\Exceptions\JsonApiException;
47
use Neomerx\JsonApi\Http\Headers\MediaType;
48
use Psr\Container\ContainerExceptionInterface;
49
use Psr\Container\ContainerInterface;
50
use Psr\Container\NotFoundExceptionInterface;
51
use Psr\Http\Message\ResponseInterface;
52
use Psr\Http\Message\UriInterface;
53
54
/**
55
 * @package Limoncello\Flute
56
 */
57
trait DefaultControllerMethodsTrait
58
{
59
    /** @noinspection PhpTooManyParametersInspection
60
     * @param array                                 $queryParams
61
     * @param UriInterface                          $requestUri
62
     * @param JsonApiQueryValidatingParserInterface $queryParser
63
     * @param ParametersMapperInterface             $mapper
64
     * @param CrudInterface                         $crud
65
     * @param SettingsProviderInterface             $provider
66
     * @param JsonSchemesInterface                  $jsonSchemes
67
     * @param EncoderInterface                      $encoder
68
     *
69
     * @return ResponseInterface
70
     */
71 12
    protected static function defaultIndexHandler(
72
        array $queryParams,
73
        UriInterface $requestUri,
74
        JsonApiQueryValidatingParserInterface $queryParser,
75
        ParametersMapperInterface $mapper,
76
        CrudInterface $crud,
77
        SettingsProviderInterface $provider,
78
        JsonSchemesInterface $jsonSchemes,
79
        EncoderInterface $encoder
80
    ): ResponseInterface {
81 12
        $queryParser->parse($queryParams);
82
83 11
        $models = $mapper->applyQueryParameters($queryParser, $crud)->index();
84
85
        $encParams = new EncodingParameters(
86
            $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...
87
            $queryParser->hasFields() === true ? $queryParser->getFields() : null
88
        );
89
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
90
        $response  = ($models->getData()) === null ?
91
            $responses->getCodeResponse(404) : $responses->getContentResponse($models);
92
93
        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 JsonSchemesInterface                  $jsonSchemes
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
        JsonSchemesInterface $jsonSchemes,
118
        EncoderInterface $encoder
119
    ): ResponseInterface {
120 1
        $queryParser->parse($queryParams);
121
122 1
        $model = $mapper->applyQueryParameters($queryParser, $crud)->read($index);
123
        assert(!($model instanceof PaginatedDataInterface));
124
125
        $encParams = new EncodingParameters(
126
            $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...
127
            $queryParser->hasFields() === true ? $queryParser->getFields() : null
128
        );
129
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
130
        $response  = $model === null ?
131
            $responses->getCodeResponse(404) : $responses->getContentResponse($model);
132
133
        return $response;
134
    }
135
136
    /** @noinspection PhpTooManyParametersInspection
137
     * @param Closure                               $apiHandler
138
     * @param array                                 $queryParams
139
     * @param UriInterface                          $requestUri
140
     * @param JsonApiQueryValidatingParserInterface $queryParser
141
     * @param ParametersMapperInterface             $mapper
142
     * @param CrudInterface                         $crud
143
     * @param SettingsProviderInterface             $provider
144
     * @param JsonSchemesInterface                  $jsonSchemes
145
     * @param EncoderInterface                      $encoder
146
     *
147
     * @return ResponseInterface
148
     */
149 2
    protected static function defaultReadRelationshipWithClosureHandler(
150
        Closure $apiHandler,
151
        array $queryParams,
152
        UriInterface $requestUri,
153
        JsonApiQueryValidatingParserInterface $queryParser,
154
        ParametersMapperInterface $mapper,
155
        CrudInterface $crud,
156
        SettingsProviderInterface $provider,
157
        JsonSchemesInterface $jsonSchemes,
158
        EncoderInterface $encoder
159
    ): ResponseInterface {
160 2
        $queryParser->parse($queryParams);
161 2
        $mapper->applyQueryParameters($queryParser, $crud);
162
163 2
        $relData = call_user_func($apiHandler);
164
165
        $encParams = new EncodingParameters(
166
            $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...
167
            $queryParser->hasFields() === true ? $queryParser->getFields() : null
168
        );
169
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
170
171
        $noData   = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null);
172
        $response = $noData === true ? $responses->getCodeResponse(404) : $responses->getContentResponse($relData);
173
174
        return $response;
175
    }
176
177
    /** @noinspection PhpTooManyParametersInspection
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 JsonSchemesInterface                  $jsonSchemes
186
     * @param EncoderInterface                      $encoder
187
     *
188
     * @return ResponseInterface
189
     */
190 1
    protected static function defaultReadRelationshipIdentifiersWithClosureHandler(
191
        Closure $apiHandler,
192
        array $queryParams,
193
        UriInterface $requestUri,
194
        JsonApiQueryValidatingParserInterface $queryParser,
195
        ParametersMapperInterface $mapper,
196
        CrudInterface $crud,
197
        SettingsProviderInterface $provider,
198
        JsonSchemesInterface $jsonSchemes,
199
        EncoderInterface $encoder
200
    ): ResponseInterface {
201 1
        $queryParser->parse($queryParams);
202 1
        $mapper->applyQueryParameters($queryParser, $crud);
203
204 1
        $relData = call_user_func($apiHandler);
205
206
        $encParams = new EncodingParameters(
207
            $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...
208
            $queryParser->hasFields() === true ? $queryParser->getFields() : null
209
        );
210
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
211
212
        $noData   = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null);
213
        $response = $noData === true ? $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData);
214
215
        return $response;
216
    }
217
218
    /** @noinspection PhpTooManyParametersInspection
219
     * @param UriInterface                         $requestUri
220
     * @param string                               $requestBody
221
     * @param string                               $schemeClass
222
     * @param ModelSchemeInfoInterface             $schemeInfo
223
     * @param JsonApiDataValidatingParserInterface $validator
224
     * @param CrudInterface                        $crud
225
     * @param SettingsProviderInterface            $provider
226
     * @param JsonSchemesInterface                 $jsonSchemes
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 $schemeClass,
239
        ModelSchemeInfoInterface $schemeInfo,
240
        JsonApiDataValidatingParserInterface $validator,
241
        CrudInterface $crud,
242
        SettingsProviderInterface $provider,
243
        JsonSchemesInterface $jsonSchemes,
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
            $schemeClass,
253 1
            $schemeInfo,
254 1
            $validator,
255 1
            $crud,
256 1
            $errorFactory,
257 1
            $formatterFactory
258
        );
259
260 1
        return static::defaultCreateResponse($index, $requestUri, $crud, $provider, $jsonSchemes, $encoder);
261
    }
262
263
    /**
264
     * @param string                               $requestBody
265
     * @param string                               $schemeClass
266
     * @param ModelSchemeInfoInterface             $schemeInfo
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 $schemeClass,
277
        ModelSchemeInfoInterface $schemeInfo,
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::mapSchemeDataToModelData($captures, $schemeClass, $schemeInfo);
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 JsonSchemesInterface      $jsonSchemes
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
        JsonSchemesInterface $jsonSchemes,
309
        EncoderInterface $encoder
310
    ): ResponseInterface {
311 1
        $model = $crud->read($index);
312
        assert($model !== null && !($model instanceof PaginatedDataInterface));
313
314
        $encParams = null;
315
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
316
        $response  = $responses->getCreatedResponse($model);
317
318
        return $response;
319
    }
320
321
    /** @noinspection PhpTooManyParametersInspection
322
     * @param string                               $index
323
     * @param UriInterface                         $requestUri
324
     * @param string                               $requestBody
325
     * @param string                               $schemeClass
326
     * @param ModelSchemeInfoInterface             $schemeInfo
327
     * @param JsonApiDataValidatingParserInterface $validator
328
     * @param CrudInterface                        $crud
329
     * @param SettingsProviderInterface            $provider
330
     * @param JsonSchemesInterface                 $jsonSchemes
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 $schemeClass,
346
        ModelSchemeInfoInterface $schemeInfo,
347
        JsonApiDataValidatingParserInterface $validator,
348
        CrudInterface $crud,
349
        SettingsProviderInterface $provider,
350
        JsonSchemesInterface $jsonSchemes,
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
            $schemeClass,
363 5
            $schemeInfo,
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, $jsonSchemes, $encoder);
373
    }
374
375
    /** @noinspection PhpTooManyParametersInspection
376
     * @param string                               $index
377
     * @param string                               $requestBody
378
     * @param string                               $schemeClass
379
     * @param ModelSchemeInfoInterface             $schemeInfo
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 $schemeClass,
396
        ModelSchemeInfoInterface $schemeInfo,
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::mapSchemeDataToModelData($captures, $schemeClass, $schemeInfo);
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 JsonSchemesInterface      $jsonSchemes
436
     * @param EncoderInterface          $encoder
437
     *
438
     * @return ResponseInterface
439
     *
440
     * @SuppressWarnings(PHPMD.ElseExpression)
441
     */
442 2
    protected static function defaultUpdateResponse(
443
        int $updated,
444
        string $index,
445
        UriInterface $requestUri,
446
        CrudInterface $crud,
447
        SettingsProviderInterface $provider,
448
        JsonSchemesInterface $jsonSchemes,
449
        EncoderInterface $encoder
450
    ): ResponseInterface {
451 2
        $encParams = null;
452 2
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
453 2
        if ($updated > 0) {
454 1
            $model = $crud->read($index);
455
            assert($model !== null && !($model instanceof PaginatedDataInterface));
456
            $response = $responses->getContentResponse($model);
457
        } else {
458 1
            $response = $responses->getCodeResponse(404);
459
        }
460
461 1
        return $response;
462
    }
463
464
    /** @noinspection PhpTooManyParametersInspection
465
     * @param string                               $parentIndex
466
     * @param string                               $modelRelName
467
     * @param string                               $childIndex
468
     * @param UriInterface                         $requestUri
469
     * @param string                               $requestBody
470
     * @param string                               $childSchemeClass
471
     * @param ModelSchemeInfoInterface             $schemeInfo
472
     * @param JsonApiDataValidatingParserInterface $childValidator
473
     * @param CrudInterface                        $parentCrud
474
     * @param CrudInterface                        $childCrud
475
     * @param SettingsProviderInterface            $provider
476
     * @param JsonSchemesInterface                 $jsonSchemes
477
     * @param EncoderInterface                     $encoder
478
     * @param FactoryInterface                     $errorFactory
479
     * @param FormatterFactoryInterface            $formatterFactory
480
     * @param string                               $messagesNamespace
481
     * @param string                               $errorMessage
482
     *
483
     * @return ResponseInterface
484
     *
485
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
486
     */
487 2
    protected static function defaultUpdateInRelationshipHandler(
488
        string $parentIndex,
489
        string $modelRelName,
490
        string $childIndex,
491
        UriInterface $requestUri,
492
        string $requestBody,
493
        string $childSchemeClass,
494
        ModelSchemeInfoInterface $schemeInfo,
495
        JsonApiDataValidatingParserInterface $childValidator,
496
        CrudInterface $parentCrud,
497
        CrudInterface $childCrud,
498
        SettingsProviderInterface $provider,
499
        JsonSchemesInterface $jsonSchemes,
500
        EncoderInterface $encoder,
501
        FactoryInterface $errorFactory,
502
        FormatterFactoryInterface $formatterFactory,
503
        string $messagesNamespace = S::GENERIC_NAMESPACE,
504
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
505
    ): ResponseInterface {
506 2
        if ($parentCrud->hasInRelationship($parentIndex, $modelRelName, $childIndex) === true) {
507
            return static::defaultUpdateHandler(
508
                $childIndex,
509
                $requestUri,
510
                $requestBody,
511
                $childSchemeClass,
512
                $schemeInfo,
513
                $childValidator,
514
                $childCrud,
515
                $provider,
516
                $jsonSchemes,
517
                $encoder,
518
                $errorFactory,
519
                $formatterFactory,
520
                $messagesNamespace,
521
                $errorMessage
522
            );
523
        }
524
525 1
        $encParams = null;
526 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
527
528 1
        return $responses->getCodeResponse(404);
529
    }
530
531
    /**
532
     * @param string                    $index
533
     * @param UriInterface              $requestUri
534
     * @param CrudInterface             $crud
535
     * @param SettingsProviderInterface $provider
536
     * @param JsonSchemesInterface      $jsonSchemes
537
     * @param EncoderInterface          $encoder
538
     *
539
     * @return ResponseInterface
540
     */
541 1
    protected static function defaultDeleteHandler(
542
        string $index,
543
        UriInterface $requestUri,
544
        CrudInterface $crud,
545
        SettingsProviderInterface $provider,
546
        JsonSchemesInterface $jsonSchemes,
547
        EncoderInterface $encoder
548
    ): ResponseInterface {
549 1
        $crud->remove($index);
550
551 1
        $encParams = null;
552 1
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
553 1
        $response  = $responses->getCodeResponse(204);
554
555 1
        return $response;
556
    }
557
558
    /** @noinspection PhpTooManyParametersInspection
559
     * @param string                    $parentIndex
560
     * @param string                    $modelRelName
561
     * @param string                    $childIndex
562
     * @param UriInterface              $requestUri
563
     * @param CrudInterface             $parentCrud
564
     * @param CrudInterface             $childCrud
565
     * @param SettingsProviderInterface $provider
566
     * @param JsonSchemesInterface      $jsonSchemes
567
     * @param EncoderInterface          $encoder
568
     *
569
     * @return ResponseInterface
570
     */
571 1
    protected static function defaultDeleteInRelationshipHandler(
572
        string $parentIndex,
573
        string $modelRelName,
574
        string $childIndex,
575
        UriInterface $requestUri,
576
        CrudInterface $parentCrud,
577
        CrudInterface $childCrud,
578
        SettingsProviderInterface $provider,
579
        JsonSchemesInterface $jsonSchemes,
580
        EncoderInterface $encoder
581
    ): ResponseInterface {
582 1
        if ($parentCrud->hasInRelationship($parentIndex, $modelRelName, $childIndex) === true) {
583
            return static::defaultDeleteHandler(
584
                $childIndex,
585
                $requestUri,
586
                $childCrud,
587
                $provider,
588
                $jsonSchemes,
589
                $encoder
590
            );
591
        }
592
593
        $encParams = null;
594
        $responses = static::defaultCreateResponses($requestUri, $provider, $jsonSchemes, $encoder, $encParams);
595
596
        return $responses->getCodeResponse(404);
597
    }
598
599
    /**
600
     * @param ContainerInterface $container
601
     * @param string             $rulesClass
602
     *
603
     * @return JsonApiQueryValidatingParserInterface
604
     * @throws ContainerExceptionInterface
605
     * @throws NotFoundExceptionInterface
606
     */
607 16
    protected static function defaultCreateQueryParser(
608
        ContainerInterface $container,
609
        string $rulesClass = DefaultQueryValidationRules::class
610
    ): JsonApiQueryValidatingParserInterface {
611 16
        static::assertClassImplements($rulesClass, JsonApiQueryRulesInterface::class);
612
613
        /** @var JsonApiParserFactoryInterface $factory */
614 16
        $factory   = $container->get(JsonApiParserFactoryInterface::class);
615 16
        $validator = $factory->createQueryParser($rulesClass);
616
617 16
        return $validator;
618
    }
619
620
    /**
621
     * @param ContainerInterface $container
622
     * @param string             $rulesClass
623
     *
624
     * @return JsonApiDataValidatingParserInterface
625
     *
626
     * @throws ContainerExceptionInterface
627
     * @throws NotFoundExceptionInterface
628
     */
629 8
    protected static function defaultCreateDataParser(
630
        ContainerInterface $container,
631
        string $rulesClass
632
    ): JsonApiDataValidatingParserInterface {
633 8
        static::assertClassImplements($rulesClass, JsonApiDataRulesInterface::class);
634
635
        /** @var JsonApiParserFactoryInterface $factory */
636 8
        $factory   = $container->get(JsonApiParserFactoryInterface::class);
637 8
        $validator = $factory->createDataParser($rulesClass);
638
639 8
        return $validator;
640
    }
641
642
    /**
643
     * @param ContainerInterface $container
644
     * @param string             $schemaClass
645
     *
646
     * @return ParametersMapperInterface
647
     * @throws ContainerExceptionInterface
648
     * @throws NotFoundExceptionInterface
649
     */
650 16
    protected static function defaultCreateParameterMapper(
651
        ContainerInterface $container,
652
        string $schemaClass
653
    ): ParametersMapperInterface {
654 16
        static::assertClassImplements($schemaClass, SchemaInterface::class);
655
656
        /** @var SchemaInterface $schemaClass */
657 16
        $jsonResourceType = $schemaClass::TYPE;
658
659
        /** @var ParametersMapperInterface $mapper */
660 16
        $mapper = $container->get(ParametersMapperInterface::class);
661 16
        $mapper->selectRootSchemeByResourceType($jsonResourceType);
662
663 16
        return $mapper;
664
    }
665
666
    /**
667
     * @param ContainerInterface $container
668
     * @param string|null        $class
669
     *
670
     * @return CrudInterface
671
     *
672
     * @throws ContainerExceptionInterface
673
     * @throws NotFoundExceptionInterface
674
     */
675 26
    protected static function defaultCreateApi(ContainerInterface $container, string $class): CrudInterface
676
    {
677 26
        static::assertClassImplements($class, CrudInterface::class);
678
679
        /** @var FactoryInterface $factory */
680 26
        $factory = $container->get(FactoryInterface::class);
681 26
        $api     = $factory->createApi($class);
682
683 26
        return $api;
684
    }
685
686
    /**
687
     * @param UriInterface                     $requestUri
688
     * @param SettingsProviderInterface        $provider
689
     * @param JsonSchemesInterface             $jsonSchemes
690
     * @param EncoderInterface                 $encoder
691
     * @param EncodingParametersInterface|null $parameters
692
     *
693
     * @return ResponsesInterface
694
     */
695 4
    protected static function defaultCreateResponses(
696
        UriInterface $requestUri,
697
        SettingsProviderInterface $provider,
698
        JsonSchemesInterface $jsonSchemes,
699
        EncoderInterface $encoder,
700
        ?EncodingParametersInterface $parameters
701
    ): ResponsesInterface {
702 4
        $encoder->forOriginalUri($requestUri);
703 4
        $settings  = $provider->get(S::class);
704 4
        $responses = new Responses(
705 4
            new MediaType(MediaTypeInterface::JSON_API_TYPE, MediaTypeInterface::JSON_API_SUB_TYPE),
706 4
            $encoder,
707 4
            $jsonSchemes,
708 4
            $parameters,
709 4
            $settings[S::KEY_URI_PREFIX],
710 4
            $settings[S::KEY_META]
711
        );
712
713 4
        return $responses;
714
    }
715
716
    /**
717
     * @param array                    $captures
718
     * @param string                   $schemeClass
719
     * @param ModelSchemeInfoInterface $schemeInfo
720
     *
721
     * @return array
722
     */
723 3
    private static function mapSchemeDataToModelData(
724
        array $captures,
725
        string $schemeClass,
726
        ModelSchemeInfoInterface $schemeInfo
727
    ): array {
728 3
        static::assertClassImplements($schemeClass, SchemaInterface::class);
729
730
        /** @var SchemaInterface $schemeClass */
731 3
        static::assertClassImplements($modelClass = $schemeClass::MODEL, ModelInterface::class);
732
        /** @var ModelInterface $modelClass */
733
734 3
        $index         = null;
735 3
        $fields        = [];
736 3
        $toManyIndexes = [];
737 3
        foreach ($captures as $name => $value) {
738 3
            assert(is_string($name) === true);
739 3
            if ($name === DI::KEYWORD_ID) {
740 3
                $index = $value;
741 3
            } elseif ($schemeClass::hasAttributeMapping($name) === true) {
742 3
                $fieldName          = $schemeClass::getAttributeMapping($name);
743 3
                $fields[$fieldName] = $value;
744 3
            } elseif ($schemeClass::hasRelationshipMapping($name) === true) {
745 2
                $modelRelName = $schemeClass::getRelationshipMapping($name);
746 2
                $relType      = $schemeInfo->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...
747 2
                if ($relType === RelationshipTypes::BELONGS_TO) {
748 2
                    $fkName          = $schemeInfo->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...
749 2
                    $fields[$fkName] = $value;
750 2
                } elseif ($relType === RelationshipTypes::BELONGS_TO_MANY) {
751 3
                    $toManyIndexes[$modelRelName] = $value;
752
                }
753
            }
754
        }
755
756 3
        $result = [$index, $fields, $toManyIndexes];
757
758 3
        return $result;
759
    }
760
761
    /**
762
     * @param string                    $requestBody
763
     * @param FactoryInterface          $errorFactory
764
     * @param FormatterFactoryInterface $formatterFactory
765
     * @param string                    $messagesNamespace
766
     * @param string                    $errorMessage
767
     *
768
     * @return array
769
     */
770 6
    private static function readJsonFromRequest(
771
        string $requestBody,
772
        FactoryInterface $errorFactory,
773
        FormatterFactoryInterface $formatterFactory,
774
        string $messagesNamespace = S::GENERIC_NAMESPACE,
775
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
776
    ): array {
777 6
        if (empty($requestBody) === true || ($json = json_decode($requestBody, true)) === null) {
778 1
            $formatter = $formatterFactory->createFormatter($messagesNamespace);
779 1
            $errors    = $errorFactory->createErrorCollection();
780 1
            $errors->addDataError($formatter->formatMessage($errorMessage));
781
782 1
            throw new JsonApiException($errors);
783
        }
784
785 5
        return $json;
786
    }
787
788
    /**
789
     * @param null|string $value
790
     *
791
     * @return void
792
     */
793 26
    private static function assertClassValueDefined(?string $value): void
794
    {
795 26
        assert(empty($value) === false, 'Value should be defined in `' . static::class . '`.');
796
    }
797
798
    /**
799
     * @param string $class
800
     * @param string $interface
801
     *
802
     * @return void
803
     */
804 26
    private static function assertClassImplements(string $class, string $interface): void
805
    {
806 26
        assert(
807 26
            array_key_exists($interface, class_implements($class)) === true,
808 26
            "Class `$class` should implement `" . $interface . '` interface.'
809
        );
810
    }
811
}
812