Completed
Push — master ( b6f561...db9bd8 )
by Neomerx
03:16
created

BaseController::updateInRelationship()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 32
Code Lines 26

Duplication

Lines 32
Ratio 100 %

Code Coverage

Tests 16
CRAP Score 2

Importance

Changes 0
Metric Value
dl 32
loc 32
ccs 16
cts 16
cp 1
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 26
nc 2
nop 8
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;
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 Limoncello\Contracts\Application\ModelInterface;
20
use Limoncello\Contracts\Data\ModelSchemeInfoInterface;
21
use Limoncello\Contracts\Data\RelationshipTypes;
22
use Limoncello\Contracts\L10n\FormatterFactoryInterface;
23
use Limoncello\Contracts\L10n\FormatterInterface;
24
use Limoncello\Flute\Contracts\Api\CrudInterface;
25
use Limoncello\Flute\Contracts\FactoryInterface;
26
use Limoncello\Flute\Contracts\Http\ControllerInterface;
27
use Limoncello\Flute\Contracts\Http\Query\ParametersMapperInterface;
28
use Limoncello\Flute\Contracts\Http\Query\QueryParserInterface;
29
use Limoncello\Flute\Contracts\Models\PaginatedDataInterface;
30
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
31
use Limoncello\Flute\Contracts\Validation\JsonApiValidatorFactoryInterface;
32
use Limoncello\Flute\Contracts\Validation\JsonApiValidatorInterface;
33
use Limoncello\Flute\Http\Traits\CreateResponsesTrait;
34
use Limoncello\Flute\L10n\Messages;
35
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as DI;
36
use Neomerx\JsonApi\Exceptions\JsonApiException;
37
use Psr\Container\ContainerInterface;
38
use Psr\Http\Message\ResponseInterface;
39
use Psr\Http\Message\ServerRequestInterface;
40
41
/**
42
 * @package Limoncello\Flute
43
 *
44
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
45
 */
46
abstract class BaseController implements ControllerInterface
47
{
48
    use CreateResponsesTrait;
49
50
    /** API class name */
51
    const API_CLASS = null;
52
53
    /** JSON API Schema class name */
54
    const SCHEMA_CLASS = null;
55
56
    /** JSON API validation rules set class */
57
    const ON_CREATE_VALIDATION_RULES_SET_CLASS = null;
58
59
    /** JSON API validation rules set class */
60
    const ON_UPDATE_VALIDATION_RULES_SET_CLASS = null;
61
62
    /**
63
     * @inheritdoc
64
     */
65 12
    public static function index(
66
        array $routeParams,
67
        ContainerInterface $container,
68
        ServerRequestInterface $request
69
    ): ResponseInterface {
70
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
71 12
        $parser = static::configureOnIndexParser(static::createQueryParser($container))
72 12
            ->parse($request->getQueryParams());
73 11
        $mapper = static::createParameterMapper($container);
74 11
        $api    = static::createApi($container);
75
76 11
        $models = $mapper->applyQueryParameters($parser, $api)->index();
77
78 11
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
79 11
        $response  = ($models->getData()) === null ?
80 11
            $responses->getCodeResponse(404) : $responses->getContentResponse($models);
81
82 11
        return $response;
83
    }
84
85
    /**
86
     * @inheritdoc
87
     */
88 1
    public static function create(
89
        array $routeParams,
90
        ContainerInterface $container,
91
        ServerRequestInterface $request
92
    ): ResponseInterface {
93 1
        list ($index, $api) = static::createImpl($container, $request);
94
95 1
        $data = $api->read($index);
96 1
        assert(!($data instanceof PaginatedDataInterface));
97
98 1
        $response = static::createResponses($container, $request)->getCreatedResponse($data);
99
100 1
        return $response;
101
    }
102
103
    /**
104
     * @inheritdoc
105
     */
106 1
    public static function read(
107
        array $routeParams,
108
        ContainerInterface $container,
109
        ServerRequestInterface $request
110
    ): ResponseInterface {
111
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
112 1
        $parser = static::configureOnReadParser(static::createQueryParser($container))
113 1
            ->parse($request->getQueryParams());
114 1
        $mapper = static::createParameterMapper($container);
115
116 1
        $index     = $routeParams[static::ROUTE_KEY_INDEX];
117 1
        $modelData = $mapper->applyQueryParameters($parser, static::createApi($container))->read($index);
118 1
        assert(!($modelData instanceof PaginatedDataInterface));
119
120 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
121 1
        $response  = $modelData === null ?
122 1
            $responses->getCodeResponse(404) : $responses->getContentResponse($modelData);
123
124 1
        return $response;
125
    }
126
127
    /**
128
     * @inheritdoc
129
     */
130 4 View Code Duplication
    public static function update(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
131
        array $routeParams,
132
        ContainerInterface $container,
133
        ServerRequestInterface $request
134
    ): ResponseInterface {
135 4
        list ($updated, $index, $api) = static::updateImpl($routeParams, $container, $request);
136
137 1
        $responses = static::createResponses($container, $request);
138 1
        if ($updated > 0) {
139 1
            $modelData = $api->read($index);
140 1
            assert(!($modelData instanceof PaginatedDataInterface));
141 1
            $response  = $responses->getContentResponse($modelData);
142
        } else {
143
            $response = $responses->getCodeResponse(404);
144
        }
145
146 1
        return $response;
147
    }
148
149
    /**
150
     * @inheritdoc
151
     */
152 1
    public static function delete(
153
        array $routeParams,
154
        ContainerInterface $container,
155
        ServerRequestInterface $request
156
    ): ResponseInterface {
157 1
        static::createApi($container)->remove($routeParams[static::ROUTE_KEY_INDEX]);
158
159 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
160
161 1
        return $response;
162
    }
163
164
    /**
165
     * @param string                 $index
166
     * @param string                 $relationshipName
167
     * @param ContainerInterface     $container
168
     * @param ServerRequestInterface $request
169
     *
170
     * @return ResponseInterface
171
     */
172 2
    protected static function readRelationship(
173
        string $index,
174
        string $relationshipName,
175
        ContainerInterface $container,
176
        ServerRequestInterface $request
177
    ): ResponseInterface {
178
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
179 2
        $parser = static::configureOnReadRelationshipParser(
180 2
            $relationshipName,
181 2
            static::createQueryParser($container))->parse($request->getQueryParams()
182
        );
183
184 2
        $relData   = static::readRelationshipData($index, $relationshipName, $container, $parser);
0 ignored issues
show
Bug introduced by
Since readRelationshipData() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of readRelationshipData() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
185 2
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
186 2
        $response  = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null) ?
187 2
            $responses->getCodeResponse(404) : $responses->getContentResponse($relData);
188
189 2
        return $response;
190
    }
191
192
    /**
193
     * @param string                 $index
194
     * @param string                 $relationshipName
195
     * @param ContainerInterface     $container
196
     * @param ServerRequestInterface $request
197
     *
198
     * @return ResponseInterface
199
     */
200 1
    protected static function readRelationshipIdentifiers(
201
        string $index,
202
        string $relationshipName,
203
        ContainerInterface $container,
204
        ServerRequestInterface $request
205
    ): ResponseInterface {
206
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
207 1
        $parser = static::configureOnReadRelationshipIdentifiersParser(
208 1
            $relationshipName,
209 1
            static::createQueryParser($container)
210 1
        )->parse($request->getQueryParams());
211
212 1
        $relData   = static::readRelationshipData($index, $relationshipName, $container, $parser);
0 ignored issues
show
Bug introduced by
Since readRelationshipData() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of readRelationshipData() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
213 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
214 1
        $response  = $relData->getData() === null ?
215 1
            $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData);
216
217 1
        return $response;
218
    }
219
220
    /**
221
     * @param ContainerInterface $container
222
     * @param string|null        $class
223
     *
224
     * @return CrudInterface
225
     */
226 21
    protected static function createApi(ContainerInterface $container, string $class = null): CrudInterface
227
    {
228
        /** @var FactoryInterface $factory */
229 21
        $factory = $container->get(FactoryInterface::class);
230 21
        $api     = $factory->createApi($class ?? static::API_CLASS);
231
232 21
        return $api;
233
    }
234
235
    /**
236
     * @param ContainerInterface     $container
237
     * @param ServerRequestInterface $request
238
     *
239
     * @return array
240
     */
241 7
    protected static function readJsonFromRequest(ContainerInterface $container, ServerRequestInterface $request): array
242
    {
243 7
        $body = (string)$request->getBody();
244 7
        if (empty($body) === true || ($json = json_decode($body, true)) === null) {
245
            /** @var FactoryInterface $factory */
246 1
            $factory = $container->get(FactoryInterface::class);
247 1
            $errors  = $factory->createErrorCollection();
248 1
            $errors->addDataError(
249 1
                static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
250
            );
251
252 1
            throw new JsonApiException($errors);
253
        }
254
255 6
        return $json;
256
    }
257
258
    /**
259
     * @param array              $routeParams
260
     * @param ContainerInterface $container
261
     * @param array              $jsonData
262
     *
263
     * @return array
264
     *
265
     * @SuppressWarnings(PHPMD.ElseExpression)
266
     */
267 3
    protected static function normalizeIndexValueOnUpdate(
268
        array $routeParams,
269
        ContainerInterface $container,
270
        array $jsonData
271
    ): array {
272
        // check that index in data and URL are identical
273 3
        $index         = $routeParams[static::ROUTE_KEY_INDEX];
274 3
        $dataSection   = null;
275
        $hasIndexValue =
276 3
            array_key_exists(DI::KEYWORD_DATA, $jsonData) &&
277 3
            array_key_exists(DI::KEYWORD_ID, ($dataSection = $jsonData[DI::KEYWORD_DATA]));
278 3
        if ($hasIndexValue === true) {
279 2
            assert($dataSection !== null);
280 2
            if ($dataSection[DI::KEYWORD_ID] !== $index) {
281
                /** @var FactoryInterface $factory */
282 1
                $factory = $container->get(FactoryInterface::class);
283 1
                $errors  = $factory->createErrorCollection();
284 1
                $errors->addDataIdError(
285 1
                    static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
286
                );
287
288 2
                throw new JsonApiException($errors);
289
            }
290
        } else {
291
            // put the index to data for our convenience
292 1
            $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] = $index;
293
        }
294
295 2
        return $jsonData;
296
    }
297
298
    /**
299
     * @param int|string             $parentIndex
300
     * @param string                 $relationshipName
301
     * @param int|string             $childIndex
302
     * @param string                 $childApiClass
303
     * @param ContainerInterface     $container
304
     * @param ServerRequestInterface $request
305
     *
306
     * @return ResponseInterface
307
     */
308 1
    protected static function deleteInRelationship(
309
        $parentIndex,
310
        string $relationshipName,
311
        $childIndex,
312
        string $childApiClass,
313
        ContainerInterface $container,
314
        ServerRequestInterface $request
315
    ): ResponseInterface {
316
        /** @var SchemaInterface $schemaClass */
317 1
        $schemaClass  = static::SCHEMA_CLASS;
318 1
        $modelRelName = $schemaClass::getRelationshipMapping($relationshipName);
319 1
        $hasChild     = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex);
320 1
        if ($hasChild === false) {
321 1
            return static::createResponses($container, $request)->getCodeResponse(404);
322
        }
323
324 1
        $childApi = static::createApi($container, $childApiClass);
325
326 1
        $childApi->remove($childIndex);
327 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
328
329 1
        return $response;
330
    }
331
332
    /** @noinspection PhpTooManyParametersInspection
333
     * @param int|string             $parentIndex
334
     * @param string                 $relationshipName
335
     * @param int|string             $childIndex
336
     * @param array                  $attributes
337
     * @param array                  $toMany
338
     * @param string                 $childApiClass
339
     * @param ContainerInterface     $container
340
     * @param ServerRequestInterface $request
341
     *
342
     * @return ResponseInterface
343
     */
344 2 View Code Duplication
    protected static function updateInRelationship(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
345
        $parentIndex,
346
        string $relationshipName,
347
        $childIndex,
348
        array $attributes,
349
        array $toMany,
350
        string $childApiClass,
351
        ContainerInterface $container,
352
        ServerRequestInterface $request
353
    ): ResponseInterface {
354
        /** @var CrudInterface $childApi */
355 2
        list ($updated, $childApi) = static::updateInRelationshipImpl(
0 ignored issues
show
Bug introduced by
Since updateInRelationshipImpl() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of updateInRelationshipImpl() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
356 2
            $parentIndex,
357 2
            $relationshipName,
358 2
            $childIndex,
359 2
            $attributes,
360 2
            $toMany,
361 2
            $childApiClass,
362 2
            $container
363
        );
364
365 2
        $responses = static::createResponses($container, $request);
366 2
        if ($updated > 0) {
367 1
            $modelData = $childApi->read($childIndex);
368 1
            assert(!($modelData instanceof PaginatedDataInterface));
369 1
            $response  = $responses->getContentResponse($modelData);
370
        } else {
371 1
            $response = $responses->getCodeResponse(404);
372
        }
373
374 2
        return $response;
375
    }
376
377
    /**
378
     * @param ContainerInterface $container
379
     *
380
     * @return JsonApiValidatorInterface
381
     */
382 1 View Code Duplication
    protected static function createOnCreateValidator(ContainerInterface $container): JsonApiValidatorInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
383
    {
384 1
        assert(
385 1
            empty(static::ON_CREATE_VALIDATION_RULES_SET_CLASS) === false,
386 1
            'Validation rules set should be defined for class ' . static::class . '.'
387
        );
388
389 1
        return static::createJsonApiValidator($container, static::ON_CREATE_VALIDATION_RULES_SET_CLASS);
390
    }
391
392
    /**
393
     * @param ContainerInterface $container
394
     *
395
     * @return JsonApiValidatorInterface
396
     */
397 2 View Code Duplication
    protected static function createOnUpdateValidator(ContainerInterface $container): JsonApiValidatorInterface
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
398
    {
399 2
        assert(
400 2
            empty(static::ON_UPDATE_VALIDATION_RULES_SET_CLASS) === false,
401 2
            'Validation rules set should be defined for class ' . static::class . '.'
402
        );
403
404 2
        return static::createJsonApiValidator($container, static::ON_UPDATE_VALIDATION_RULES_SET_CLASS);
405
    }
406
407
    /**
408
     * @param ContainerInterface $container
409
     * @param string             $rulesSetClass
410
     *
411
     * @return JsonApiValidatorInterface
412
     */
413 5
    protected static function createJsonApiValidator(
414
        ContainerInterface $container,
415
        string $rulesSetClass
416
    ): JsonApiValidatorInterface {
417
        /** @var JsonApiValidatorFactoryInterface $validatorFactory */
418 5
        $validatorFactory = $container->get(JsonApiValidatorFactoryInterface::class);
419 5
        $validator        = $validatorFactory->createValidator($rulesSetClass);
420
421 5
        return $validator;
422
    }
423
424
    /**
425
     * @param ContainerInterface $container
426
     *
427
     * @return QueryParserInterface
428
     */
429 16
    protected static function createQueryParser(ContainerInterface $container): QueryParserInterface
430
    {
431 16
        return $container->get(QueryParserInterface::class);
432
    }
433
434
    /**
435
     * @param QueryParserInterface $parser
436
     *
437
     * @return QueryParserInterface
438
     */
439 12
    protected static function configureOnIndexParser(QueryParserInterface $parser): QueryParserInterface
440
    {
441 12
        return $parser;
442
    }
443
444
    /**
445
     * @param QueryParserInterface $parser
446
     *
447
     * @return QueryParserInterface
448
     */
449 1
    protected static function configureOnReadParser(QueryParserInterface $parser): QueryParserInterface
450
    {
451 1
        return $parser;
452
    }
453
454
    /**
455
     * @param string               $name
456
     * @param QueryParserInterface $parser
457
     *
458
     * @return QueryParserInterface
459
     *
460
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
461
     */
462 2
    protected static function configureOnReadRelationshipParser(/** @noinspection PhpUnusedParameterInspection */
463
        string $name,
0 ignored issues
show
Unused Code introduced by
The parameter $name is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
464
        QueryParserInterface $parser
465
    ): QueryParserInterface {
466 2
        return $parser;
467
    }
468
469
    /**
470
     * @param string               $name
471
     * @param QueryParserInterface $parser
472
     *
473
     * @return QueryParserInterface
474
     *
475
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
476
     */
477 1
    protected static function configureOnReadRelationshipIdentifiersParser(
478
        /** @noinspection PhpUnusedParameterInspection */
479
        string $name,
0 ignored issues
show
Unused Code introduced by
The parameter $name is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
480
        QueryParserInterface $parser
481
    ): QueryParserInterface {
482 1
        return $parser;
483
    }
484
485
    /**
486
     * @param ContainerInterface $container
487
     *
488
     * @return ParametersMapperInterface
489
     */
490 15
    protected static function createParameterMapper(ContainerInterface $container): ParametersMapperInterface
491
    {
492
        /** @var SchemaInterface $schemaClass */
493 15
        $schemaClass = static::SCHEMA_CLASS;
494
495
        /** @var ParametersMapperInterface $mapper */
496 15
        $mapper = $container->get(ParametersMapperInterface::class);
497 15
        $mapper->selectRootSchemeByResourceType($schemaClass::TYPE);
498
499 15
        return $mapper;
500
    }
501
502
    /**
503
     * @param ContainerInterface $container
504
     * @param array              $captures
505
     * @param string             $schemeClass
506
     *
507
     * @return array
508
     */
509 4
    protected static function mapSchemeDataToModelData(
510
        ContainerInterface $container,
511
        array $captures,
512
        string $schemeClass
513
    ): array {
514 4
        assert(in_array(SchemaInterface::class, class_implements($schemeClass)));
515
        /** @var SchemaInterface $schemeClass */
516
517 4
        $modelClass = $schemeClass::MODEL;
518 4
        assert(in_array(ModelInterface::class, class_implements($modelClass)));
519
        /** @var ModelInterface $modelClass */
520
521
        /** @var ModelSchemeInfoInterface $schemeInfo */
522 4
        $schemeInfo = $container->get(ModelSchemeInfoInterface::class);
523
524 4
        $index         = null;
525 4
        $fields        = [];
526 4
        $toManyIndexes = [];
527 4
        foreach ($captures as $name => $value) {
528 4
            if ($name === DI::KEYWORD_ID) {
529 4
                $index = $value;
530 4
            } elseif ($schemeClass::hasAttributeMapping($name) === true) {
531 4
                $fieldName          = $schemeClass::getAttributeMapping($name);
532 4
                $fields[$fieldName] = $value;
533 4
            } elseif ($schemeClass::hasRelationshipMapping($name) === true) {
534 2
                $modelRelName = $schemeClass::getRelationshipMapping($name);
535 2
                $relType      = $schemeInfo->getRelationshipType($modelClass, $modelRelName);
536 2
                if ($relType === RelationshipTypes::BELONGS_TO) {
537 2
                    $fkName          = $schemeInfo->getForeignKey($modelClass, $modelRelName);
538 2
                    $fields[$fkName] = $value;
539 2
                } elseif ($relType === RelationshipTypes::BELONGS_TO_MANY) {
540 4
                    $toManyIndexes[$modelRelName] = $value;
541
                }
542
            }
543
        }
544
545 4
        $result = [$index, $fields, $toManyIndexes];
546
547 4
        return $result;
548
    }
549
550
    /**
551
     * @param string               $index
552
     * @param string               $relationshipName
553
     * @param ContainerInterface   $container
554
     * @param QueryParserInterface $parser
555
     *
556
     * @return PaginatedDataInterface|mixed|null
557
     */
558 3
    private static function readRelationshipData(
559
        string $index,
560
        string $relationshipName,
561
        ContainerInterface $container,
562
        QueryParserInterface $parser
563
    ) {
564 3
        $mapper = static::createParameterMapper($container);
565 3
        $api    = static::createApi($container);
566
567
        $relData = $mapper
568 3
            ->applyQueryParameters($parser, $api)
569 3
            ->readRelationship($index, $relationshipName);
570
571 3
        return $relData;
572
    }
573
574
    /**
575
     * @param ContainerInterface     $container
576
     * @param ServerRequestInterface $request
577
     *
578
     * @return array
579
     */
580 1
    protected static function createImpl(
581
        ContainerInterface $container,
582
        ServerRequestInterface $request
583
    ): array {
584 1
        $jsonData  = static::readJsonFromRequest($container, $request);
585 1
        $validator = static::createOnCreateValidator($container);
586 1
        $captures  = $validator->assert($jsonData)->getJsonApiCaptures();
587
588
        list ($index, $attributes, $toMany) =
589 1
            static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS);
590
591 1
        $api   = static::createApi($container);
592 1
        $index = $api->create($index, $attributes, $toMany);
593
594 1
        return [$index, $api];
595
    }
596
597
    /**
598
     * @param array                  $routeParams
599
     * @param ContainerInterface     $container
600
     * @param ServerRequestInterface $request
601
     *
602
     * @return array [int $updated, string $index, CrudInterface $api]
603
     */
604 4
    protected static function updateImpl(
605
        array $routeParams,
606
        ContainerInterface $container,
607
        ServerRequestInterface $request
608
    ): array {
609 4
        $jsonData  = static::normalizeIndexValueOnUpdate(
610 4
            $routeParams,
611 4
            $container,
612 4
            static::readJsonFromRequest($container, $request)
613
        );
614 2
        $validator = static::createOnUpdateValidator($container);
615 2
        $captures  = $validator->assert($jsonData)->getJsonApiCaptures();
616
617
        list ($index, $attributes, $toMany) =
618 1
            static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS);
619 1
        $api = static::createApi($container);
620
621 1
        $updated = $api->update($index, $attributes, $toMany);
622
623 1
        return [$updated, $index, $api];
624
    }
625
626
    /**
627
     * @param                        $parentIndex
628
     * @param string                 $relationshipName
629
     * @param                        $childIndex
630
     * @param array                  $attributes
631
     * @param array                  $toMany
632
     * @param string                 $childApiClass
633
     * @param ContainerInterface     $container
634
     *
635
     * @return array
636
     */
637 2
    private static function updateInRelationshipImpl(
638
        $parentIndex,
639
        string $relationshipName,
640
        $childIndex,
641
        array $attributes,
642
        array $toMany,
643
        string $childApiClass,
644
        ContainerInterface $container
645
    ): array {
646
        /** @var SchemaInterface $schemaClass */
647 2
        $schemaClass  = static::SCHEMA_CLASS;
648 2
        $modelRelName = $schemaClass::getRelationshipMapping($relationshipName);
649 2
        $hasChild     = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex);
650 2
        if ($hasChild === false) {
651 1
            return [0, null];
652
        }
653
654 1
        $childApi = static::createApi($container, $childApiClass);
655
656 1
        $updated = $childApi->update($childIndex, $attributes, $toMany);
0 ignored issues
show
Documentation introduced by
$attributes is of type array, but the function expects a object<Limoncello\Flute\Contracts\Api\iterable>.

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...
Documentation introduced by
$toMany is of type array, but the function expects a object<Limoncello\Flute\Contracts\Api\iterable>.

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...
657
658 1
        return [$updated, $childApi];
659
    }
660
661
    /**
662
     * @param ContainerInterface $container
663
     * @param string             $namespace
664
     *
665
     * @return FormatterInterface
666
     */
667 2
    protected static function createMessageFormatter(
668
        ContainerInterface $container,
669
        string $namespace = Messages::RESOURCES_NAMESPACE
670
    ): FormatterInterface {
671
        /** @var FormatterFactoryInterface $factory */
672 2
        $factory          = $container->get(FormatterFactoryInterface::class);
673 2
        $messageFormatter = $factory->createFormatter($namespace);
674
675 2
        return $messageFormatter;
676
    }
677
}
678