Completed
Push — develop ( a2b4be...d107a0 )
by Neomerx
03:39 queued 01:56
created

BaseController::updateInRelationship()   B

Complexity

Conditions 2
Paths 2

Size

Total Lines 31
Code Lines 24

Duplication

Lines 31
Ratio 100 %

Code Coverage

Tests 15
CRAP Score 2

Importance

Changes 0
Metric Value
dl 31
loc 31
ccs 15
cts 15
cp 1
rs 8.8571
c 0
b 0
f 0
cc 2
eloc 24
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.TooManyMethods)
45
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
46
 */
47
abstract class BaseController implements ControllerInterface
48
{
49
    use CreateResponsesTrait;
50
51
    /** API class name */
52
    const API_CLASS = null;
53
54
    /** JSON API Schema class name */
55
    const SCHEMA_CLASS = null;
56
57
    /** JSON API validation rules set class */
58
    const ON_CREATE_VALIDATION_RULES_SET_CLASS = null;
59
60
    /** JSON API validation rules set class */
61
    const ON_UPDATE_VALIDATION_RULES_SET_CLASS = null;
62
63
    /**
64
     * @inheritdoc
65
     */
66 12
    public static function index(
67
        array $routeParams,
68
        ContainerInterface $container,
69
        ServerRequestInterface $request
70
    ): ResponseInterface {
71
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
72 12
        $parser = static::configureOnIndexParser(
73 12
            static::createQueryParser($container)
74 12
        )->parse($request->getQueryParams());
75 11
        $mapper = static::createParameterMapper($container);
76 11
        $api    = static::createApi($container);
77
78 11
        $models = $mapper->applyQueryParameters($parser, $api)->index();
79
80 11
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
81 11
        $response  = ($models->getData()) === null ?
82 11
            $responses->getCodeResponse(404) : $responses->getContentResponse($models);
83
84 11
        return $response;
85
    }
86
87
    /**
88
     * @inheritdoc
89
     */
90 1
    public static function create(
91
        array $routeParams,
92
        ContainerInterface $container,
93
        ServerRequestInterface $request
94
    ): ResponseInterface {
95 1
        list ($index, $api) = static::createImpl($container, $request);
96
97 1
        $data = $api->read($index);
98 1
        assert(!($data instanceof PaginatedDataInterface));
99
100 1
        $response = static::createResponses($container, $request)->getCreatedResponse($data);
101
102 1
        return $response;
103
    }
104
105
    /**
106
     * @inheritdoc
107
     */
108 1
    public static function read(
109
        array $routeParams,
110
        ContainerInterface $container,
111
        ServerRequestInterface $request
112
    ): ResponseInterface {
113
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
114 1
        $parser = static::configureOnReadParser(
115 1
            static::createQueryParser($container)
116 1
        )->parse($request->getQueryParams());
117 1
        $mapper = static::createParameterMapper($container);
118
119 1
        $index     = $routeParams[static::ROUTE_KEY_INDEX];
120 1
        $modelData = $mapper->applyQueryParameters($parser, static::createApi($container))->read($index);
121 1
        assert(!($modelData instanceof PaginatedDataInterface));
122
123 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
124 1
        $response  = $modelData === null ?
125 1
            $responses->getCodeResponse(404) : $responses->getContentResponse($modelData);
126
127 1
        return $response;
128
    }
129
130
    /**
131
     * @inheritdoc
132
     */
133 5 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...
134
        array $routeParams,
135
        ContainerInterface $container,
136
        ServerRequestInterface $request
137
    ): ResponseInterface {
138 5
        list ($updated, $index, $api) = static::updateImpl($routeParams, $container, $request);
139
140 2
        $responses = static::createResponses($container, $request);
141 2
        if ($updated > 0) {
142 1
            $modelData = $api->read($index);
143 1
            assert(!($modelData instanceof PaginatedDataInterface));
144
145 1
            return $responses->getContentResponse($modelData);
146
        }
147
148 1
        return $responses->getCodeResponse(404);
149
    }
150
151
    /**
152
     * @inheritdoc
153
     */
154 1
    public static function delete(
155
        array $routeParams,
156
        ContainerInterface $container,
157
        ServerRequestInterface $request
158
    ): ResponseInterface {
159 1
        static::createApi($container)->remove($routeParams[static::ROUTE_KEY_INDEX]);
160
161 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
162
163 1
        return $response;
164
    }
165
166
    /**
167
     * @param string                 $index
168
     * @param string                 $relationshipName
169
     * @param ContainerInterface     $container
170
     * @param ServerRequestInterface $request
171
     *
172
     * @return ResponseInterface
173
     */
174 2
    protected static function readRelationship(
175
        string $index,
176
        string $relationshipName,
177
        ContainerInterface $container,
178
        ServerRequestInterface $request
179
    ): ResponseInterface {
180
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
181 2
        $parser = static::configureOnReadRelationshipParser(
182 2
            $relationshipName,
183 2
            static::createQueryParser($container)
184 2
        )->parse($request->getQueryParams());
185
186 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...
187 2
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
188 2
        $response  = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null) ?
189 2
            $responses->getCodeResponse(404) : $responses->getContentResponse($relData);
190
191 2
        return $response;
192
    }
193
194
    /**
195
     * @param string                 $index
196
     * @param string                 $relationshipName
197
     * @param ContainerInterface     $container
198
     * @param ServerRequestInterface $request
199
     *
200
     * @return ResponseInterface
201
     */
202 1
    protected static function readRelationshipIdentifiers(
203
        string $index,
204
        string $relationshipName,
205
        ContainerInterface $container,
206
        ServerRequestInterface $request
207
    ): ResponseInterface {
208
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
209 1
        $parser = static::configureOnReadRelationshipIdentifiersParser(
210 1
            $relationshipName,
211 1
            static::createQueryParser($container)
212 1
        )->parse($request->getQueryParams());
213
214 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...
215 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
216 1
        $response  = $relData->getData() === null ?
217 1
            $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData);
218
219 1
        return $response;
220
    }
221
222
    /**
223
     * @param ContainerInterface $container
224
     * @param string|null        $class
225
     *
226
     * @return CrudInterface
227
     */
228 22
    protected static function createApi(ContainerInterface $container, string $class = null): CrudInterface
229
    {
230
        /** @var FactoryInterface $factory */
231 22
        $factory = $container->get(FactoryInterface::class);
232 22
        $api     = $factory->createApi($class ?? static::API_CLASS);
233
234 22
        return $api;
235
    }
236
237
    /**
238
     * @param ContainerInterface     $container
239
     * @param ServerRequestInterface $request
240
     *
241
     * @return array
242
     */
243 8
    protected static function readJsonFromRequest(ContainerInterface $container, ServerRequestInterface $request): array
244
    {
245 8
        $body = (string)$request->getBody();
246 8
        if (empty($body) === true || ($json = json_decode($body, true)) === null) {
247
            /** @var FactoryInterface $factory */
248 1
            $factory = $container->get(FactoryInterface::class);
249 1
            $errors  = $factory->createErrorCollection();
250 1
            $errors->addDataError(
251 1
                static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
252
            );
253
254 1
            throw new JsonApiException($errors);
255
        }
256
257 7
        return $json;
258
    }
259
260
    /**
261
     * @param array              $routeParams
262
     * @param ContainerInterface $container
263
     * @param array              $jsonData
264
     *
265
     * @return array
266
     *
267
     * @SuppressWarnings(PHPMD.ElseExpression)
268
     */
269 4
    protected static function normalizeIndexValueOnUpdate(
270
        array $routeParams,
271
        ContainerInterface $container,
272
        array $jsonData
273
    ): array {
274
        // check that index in data and URL are identical
275 4
        $index         = $routeParams[static::ROUTE_KEY_INDEX];
276 4
        $dataSection   = null;
277
        $hasIndexValue =
278 4
            array_key_exists(DI::KEYWORD_DATA, $jsonData) &&
279 4
            array_key_exists(DI::KEYWORD_ID, ($dataSection = $jsonData[DI::KEYWORD_DATA]));
280 4
        if ($hasIndexValue === true) {
281 3
            assert($dataSection !== null);
282 3
            if ($dataSection[DI::KEYWORD_ID] !== $index) {
283
                /** @var FactoryInterface $factory */
284 1
                $factory = $container->get(FactoryInterface::class);
285 1
                $errors  = $factory->createErrorCollection();
286 1
                $errors->addDataIdError(
287 1
                    static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
288
                );
289
290 3
                throw new JsonApiException($errors);
291
            }
292
        } else {
293
            // put the index to data for our convenience
294 1
            $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] = $index;
295
        }
296
297 3
        return $jsonData;
298
    }
299
300
    /**
301
     * @param int|string             $parentIndex
302
     * @param string                 $relationshipName
303
     * @param int|string             $childIndex
304
     * @param string                 $childApiClass
305
     * @param ContainerInterface     $container
306
     * @param ServerRequestInterface $request
307
     *
308
     * @return ResponseInterface
309
     */
310 1
    protected static function deleteInRelationship(
311
        $parentIndex,
312
        string $relationshipName,
313
        $childIndex,
314
        string $childApiClass,
315
        ContainerInterface $container,
316
        ServerRequestInterface $request
317
    ): ResponseInterface {
318
        /** @var SchemaInterface $schemaClass */
319 1
        $schemaClass  = static::SCHEMA_CLASS;
320 1
        $modelRelName = $schemaClass::getRelationshipMapping($relationshipName);
321 1
        $hasChild     = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex);
322 1
        if ($hasChild === false) {
323 1
            return static::createResponses($container, $request)->getCodeResponse(404);
324
        }
325
326 1
        $childApi = static::createApi($container, $childApiClass);
327
328 1
        $childApi->remove($childIndex);
329 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
330
331 1
        return $response;
332
    }
333
334
    /** @noinspection PhpTooManyParametersInspection
335
     * @param int|string             $parentIndex
336
     * @param string                 $relationshipName
337
     * @param int|string             $childIndex
338
     * @param array                  $attributes
339
     * @param array                  $toMany
340
     * @param string                 $childApiClass
341
     * @param ContainerInterface     $container
342
     * @param ServerRequestInterface $request
343
     *
344
     * @return ResponseInterface
345
     */
346 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...
347
        $parentIndex,
348
        string $relationshipName,
349
        $childIndex,
350
        array $attributes,
351
        array $toMany,
352
        string $childApiClass,
353
        ContainerInterface $container,
354
        ServerRequestInterface $request
355
    ): ResponseInterface {
356
        /** @var CrudInterface $childApi */
357 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...
358 2
            $parentIndex,
359 2
            $relationshipName,
360 2
            $childIndex,
361 2
            $attributes,
362 2
            $toMany,
363 2
            $childApiClass,
364 2
            $container
365
        );
366
367 2
        $responses = static::createResponses($container, $request);
368 2
        if ($updated > 0) {
369 1
            $modelData = $childApi->read($childIndex);
370 1
            assert(!($modelData instanceof PaginatedDataInterface));
371
372 1
            return $responses->getContentResponse($modelData);
373
        }
374
375 1
        return $responses->getCodeResponse(404);
376
    }
377
378
    /**
379
     * @param ContainerInterface $container
380
     *
381
     * @return JsonApiValidatorInterface
382
     */
383 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...
384
    {
385 1
        assert(
386 1
            empty(static::ON_CREATE_VALIDATION_RULES_SET_CLASS) === false,
387 1
            'Validation rules set should be defined for class ' . static::class . '.'
388
        );
389
390 1
        return static::createJsonApiValidator($container, static::ON_CREATE_VALIDATION_RULES_SET_CLASS);
391
    }
392
393
    /**
394
     * @param ContainerInterface $container
395
     *
396
     * @return JsonApiValidatorInterface
397
     */
398 3 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...
399
    {
400 3
        assert(
401 3
            empty(static::ON_UPDATE_VALIDATION_RULES_SET_CLASS) === false,
402 3
            'Validation rules set should be defined for class ' . static::class . '.'
403
        );
404
405 3
        return static::createJsonApiValidator($container, static::ON_UPDATE_VALIDATION_RULES_SET_CLASS);
406
    }
407
408
    /**
409
     * @param ContainerInterface $container
410
     * @param string             $rulesSetClass
411
     *
412
     * @return JsonApiValidatorInterface
413
     */
414 6
    protected static function createJsonApiValidator(
415
        ContainerInterface $container,
416
        string $rulesSetClass
417
    ): JsonApiValidatorInterface {
418
        /** @var JsonApiValidatorFactoryInterface $validatorFactory */
419 6
        $validatorFactory = $container->get(JsonApiValidatorFactoryInterface::class);
420 6
        $validator        = $validatorFactory->createValidator($rulesSetClass);
421
422 6
        return $validator;
423
    }
424
425
    /**
426
     * @param ContainerInterface $container
427
     *
428
     * @return QueryParserInterface
429
     */
430 16
    protected static function createQueryParser(ContainerInterface $container): QueryParserInterface
431
    {
432 16
        return $container->get(QueryParserInterface::class);
433
    }
434
435
    /**
436
     * @param QueryParserInterface $parser
437
     *
438
     * @return QueryParserInterface
439
     */
440 12
    protected static function configureOnIndexParser(QueryParserInterface $parser): QueryParserInterface
441
    {
442 12
        return $parser;
443
    }
444
445
    /**
446
     * @param QueryParserInterface $parser
447
     *
448
     * @return QueryParserInterface
449
     */
450 1
    protected static function configureOnReadParser(QueryParserInterface $parser): QueryParserInterface
451
    {
452 1
        return $parser;
453
    }
454
455
    /**
456
     * @param string               $name
457
     * @param QueryParserInterface $parser
458
     *
459
     * @return QueryParserInterface
460
     *
461
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
462
     */
463 2
    protected static function configureOnReadRelationshipParser(
464
        /** @noinspection PhpUnusedParameterInspection */ 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...
465
        QueryParserInterface $parser
466
    ): QueryParserInterface {
467 2
        return $parser;
468
    }
469
470
    /**
471
     * @param string               $name
472
     * @param QueryParserInterface $parser
473
     *
474
     * @return QueryParserInterface
475
     *
476
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
477
     */
478 1
    protected static function configureOnReadRelationshipIdentifiersParser(
479
        /** @noinspection PhpUnusedParameterInspection */ 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 5
    protected static function mapSchemeDataToModelData(
510
        ContainerInterface $container,
511
        array $captures,
512
        string $schemeClass
513
    ): array {
514 5
        assert(in_array(SchemaInterface::class, class_implements($schemeClass)));
515
        /** @var SchemaInterface $schemeClass */
516
517 5
        $modelClass = $schemeClass::MODEL;
518 5
        assert(in_array(ModelInterface::class, class_implements($modelClass)));
519
        /** @var ModelInterface $modelClass */
520
521
        /** @var ModelSchemeInfoInterface $schemeInfo */
522 5
        $schemeInfo = $container->get(ModelSchemeInfoInterface::class);
523
524 5
        $index         = null;
525 5
        $fields        = [];
526 5
        $toManyIndexes = [];
527 5
        foreach ($captures as $name => $value) {
528 5
            if ($name === DI::KEYWORD_ID) {
529 5
                $index = $value;
530 5
            } elseif ($schemeClass::hasAttributeMapping($name) === true) {
531 5
                $fieldName          = $schemeClass::getAttributeMapping($name);
532 5
                $fields[$fieldName] = $value;
533 5
            } 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 5
                    $toManyIndexes[$modelRelName] = $value;
541
                }
542
            }
543
        }
544
545 5
        $result = [$index, $fields, $toManyIndexes];
546
547 5
        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 5
    protected static function updateImpl(
605
        array $routeParams,
606
        ContainerInterface $container,
607
        ServerRequestInterface $request
608
    ): array {
609 5
        $jsonData  = static::normalizeIndexValueOnUpdate(
610 5
            $routeParams,
611 5
            $container,
612 5
            static::readJsonFromRequest($container, $request)
613
        );
614 3
        $validator = static::createOnUpdateValidator($container);
615 3
        $captures  = $validator->assert($jsonData)->getJsonApiCaptures();
616
617
        list ($index, $attributes, $toMany) =
618 2
            static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS);
619 2
        $api = static::createApi($container);
620
621 2
        $updated = $api->update($index, $attributes, $toMany);
622
623 2
        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