Completed
Push — develop ( 0fd0fb...9d9666 )
by Neomerx
02:16
created

BaseController   C

Complexity

Total Complexity 46

Size/Duplication

Total Lines 627
Duplicated Lines 22.49 %

Coupling/Cohesion

Components 1
Dependencies 17

Test Coverage

Coverage 99.5%

Importance

Changes 0
Metric Value
wmc 46
lcom 1
cbo 17
dl 141
loc 627
ccs 199
cts 200
cp 0.995
rs 6.0016
c 0
b 0
f 0

27 Methods

Rating   Name   Duplication   Size   Complexity  
A createApi() 0 8 1
A index() 19 19 2
A create() 0 13 1
A read() 19 19 2
A update() 17 17 2
A delete() 0 11 1
A readRelationship() 19 19 2
A readRelationshipIdentifiers() 19 19 2
A readJsonFromRequest() 0 16 3
B normalizeIndexValueOnUpdate() 0 30 4
A deleteInRelationship() 0 23 2
B updateInRelationship() 30 30 2
A createOnCreateValidator() 9 9 1
A createOnUpdateValidator() 9 9 1
A createJsonApiValidator() 0 10 1
A createQueryParser() 0 4 1
A configureOnIndexParser() 0 4 1
A configureOnReadParser() 0 4 1
A configureOnReadRelationshipParser() 0 6 1
A configureOnReadRelationshipIdentifiersParser() 0 7 1
A createParameterMapper() 0 11 1
C mapSchemeDataToModelData() 0 40 7
A readRelationshipData() 0 15 1
A createImpl() 0 16 1
A updateImpl() 0 21 1
A updateInRelationshipImpl() 0 23 2
A createMessageFormatter() 0 10 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like BaseController 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 BaseController, and based on these observations, apply Extract Interface, too.

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 View Code Duplication
    public static function index(
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...
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)->getData();
96
97 1
        $response = static::createResponses($container, $request)->getCreatedResponse($data);
98
99 1
        return $response;
100
    }
101
102
    /**
103
     * @inheritdoc
104
     */
105 1 View Code Duplication
    public static function read(
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...
106
        array $routeParams,
107
        ContainerInterface $container,
108
        ServerRequestInterface $request
109
    ): ResponseInterface {
110
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
111 1
        $parser = static::configureOnReadParser(static::createQueryParser($container))
112 1
            ->parse($request->getQueryParams());
113 1
        $mapper = static::createParameterMapper($container);
114
115 1
        $index     = $routeParams[static::ROUTE_KEY_INDEX];
116 1
        $modelData = $mapper->applyQueryParameters($parser, static::createApi($container))->read($index)->getData();
117
118 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
119 1
        $response  = $modelData === null ?
120 1
            $responses->getCodeResponse(404) : $responses->getContentResponse($modelData);
121
122 1
        return $response;
123
    }
124
125
    /**
126
     * @inheritdoc
127
     */
128 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...
129
        array $routeParams,
130
        ContainerInterface $container,
131
        ServerRequestInterface $request
132
    ): ResponseInterface {
133 4
        list ($updated, $index, $api) = static::updateImpl($routeParams, $container, $request);
134
135 1
        $responses = static::createResponses($container, $request);
136 1
        if ($updated > 0) {
137 1
            $modelData = $api->read($index)->getData();
138 1
            $response  = $responses->getContentResponse($modelData);
139
        } else {
140
            $response = $responses->getCodeResponse(404);
141
        }
142
143 1
        return $response;
144
    }
145
146
    /**
147
     * @inheritdoc
148
     */
149 1
    public static function delete(
150
        array $routeParams,
151
        ContainerInterface $container,
152
        ServerRequestInterface $request
153
    ): ResponseInterface {
154 1
        static::createApi($container)->remove($routeParams[static::ROUTE_KEY_INDEX]);
155
156 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
157
158 1
        return $response;
159
    }
160
161
    /**
162
     * @param string                 $index
163
     * @param string                 $relationshipName
164
     * @param ContainerInterface     $container
165
     * @param ServerRequestInterface $request
166
     *
167
     * @return ResponseInterface
168
     */
169 2 View Code Duplication
    protected static function readRelationship(
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...
170
        string $index,
171
        string $relationshipName,
172
        ContainerInterface $container,
173
        ServerRequestInterface $request
174
    ): ResponseInterface {
175
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
176 2
        $parser = static::configureOnReadRelationshipParser(
177 2
            $relationshipName,
178 2
            static::createQueryParser($container))->parse($request->getQueryParams()
179
        );
180
181 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...
182 2
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
183 2
        $response  = $relData->getData() === null ?
184 2
            $responses->getCodeResponse(404) : $responses->getContentResponse($relData);
185
186 2
        return $response;
187
    }
188
189
    /**
190
     * @param string                 $index
191
     * @param string                 $relationshipName
192
     * @param ContainerInterface     $container
193
     * @param ServerRequestInterface $request
194
     *
195
     * @return ResponseInterface
196
     */
197 1 View Code Duplication
    protected static function readRelationshipIdentifiers(
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...
198
        string $index,
199
        string $relationshipName,
200
        ContainerInterface $container,
201
        ServerRequestInterface $request
202
    ): ResponseInterface {
203
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
204 1
        $parser = static::configureOnReadRelationshipIdentifiersParser(
205 1
            $relationshipName,
206 1
            static::createQueryParser($container)
207 1
        )->parse($request->getQueryParams());
208
209 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...
210 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
211 1
        $response  = $relData->getData() === null ?
212 1
            $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData);
213
214 1
        return $response;
215
    }
216
217
    /**
218
     * @param ContainerInterface $container
219
     * @param string|null        $class
220
     *
221
     * @return CrudInterface
222
     */
223 21
    protected static function createApi(ContainerInterface $container, string $class = null): CrudInterface
224
    {
225
        /** @var FactoryInterface $factory */
226 21
        $factory = $container->get(FactoryInterface::class);
227 21
        $api     = $factory->createApi($class ?? static::API_CLASS);
228
229 21
        return $api;
230
    }
231
232
    /**
233
     * @param ContainerInterface     $container
234
     * @param ServerRequestInterface $request
235
     *
236
     * @return array
237
     */
238 7
    protected static function readJsonFromRequest(ContainerInterface $container, ServerRequestInterface $request): array
239
    {
240 7
        $body = (string)$request->getBody();
241 7
        if (empty($body) === true || ($json = json_decode($body, true)) === null) {
242
            /** @var FactoryInterface $factory */
243 1
            $factory = $container->get(FactoryInterface::class);
244 1
            $errors  = $factory->createErrorCollection();
245 1
            $errors->addDataError(
246 1
                static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
247
            );
248
249 1
            throw new JsonApiException($errors);
250
        }
251
252 6
        return $json;
253
    }
254
255
    /**
256
     * @param array              $routeParams
257
     * @param ContainerInterface $container
258
     * @param array              $jsonData
259
     *
260
     * @return array
261
     *
262
     * @SuppressWarnings(PHPMD.ElseExpression)
263
     */
264 3
    protected static function normalizeIndexValueOnUpdate(
265
        array $routeParams,
266
        ContainerInterface $container,
267
        array $jsonData
268
    ): array {
269
        // check that index in data and URL are identical
270 3
        $index         = $routeParams[static::ROUTE_KEY_INDEX];
271 3
        $dataSection   = null;
272
        $hasIndexValue =
273 3
            array_key_exists(DI::KEYWORD_DATA, $jsonData) &&
274 3
            array_key_exists(DI::KEYWORD_ID, ($dataSection = $jsonData[DI::KEYWORD_DATA]));
275 3
        if ($hasIndexValue === true) {
276 2
            assert($dataSection !== null);
277 2
            if ($dataSection[DI::KEYWORD_ID] !== $index) {
278
                /** @var FactoryInterface $factory */
279 1
                $factory = $container->get(FactoryInterface::class);
280 1
                $errors  = $factory->createErrorCollection();
281 1
                $errors->addDataIdError(
282 1
                    static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
283
                );
284
285 2
                throw new JsonApiException($errors);
286
            }
287
        } else {
288
            // put the index to data for our convenience
289 1
            $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] = $index;
290
        }
291
292 2
        return $jsonData;
293
    }
294
295
    /**
296
     * @param int|string             $parentIndex
297
     * @param string                 $relationshipName
298
     * @param int|string             $childIndex
299
     * @param string                 $childApiClass
300
     * @param ContainerInterface     $container
301
     * @param ServerRequestInterface $request
302
     *
303
     * @return ResponseInterface
304
     */
305 1
    protected static function deleteInRelationship(
306
        $parentIndex,
307
        string $relationshipName,
308
        $childIndex,
309
        string $childApiClass,
310
        ContainerInterface $container,
311
        ServerRequestInterface $request
312
    ): ResponseInterface {
313
        /** @var SchemaInterface $schemaClass */
314 1
        $schemaClass  = static::SCHEMA_CLASS;
315 1
        $modelRelName = $schemaClass::getRelationshipMapping($relationshipName);
316 1
        $hasChild     = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex);
317 1
        if ($hasChild === false) {
318 1
            return static::createResponses($container, $request)->getCodeResponse(404);
319
        }
320
321 1
        $childApi = static::createApi($container, $childApiClass);
322
323 1
        $childApi->remove($childIndex);
324 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
325
326 1
        return $response;
327
    }
328
329
    /** @noinspection PhpTooManyParametersInspection
330
     * @param int|string             $parentIndex
331
     * @param string                 $relationshipName
332
     * @param int|string             $childIndex
333
     * @param array                  $attributes
334
     * @param array                  $toMany
335
     * @param string                 $childApiClass
336
     * @param ContainerInterface     $container
337
     * @param ServerRequestInterface $request
338
     *
339
     * @return ResponseInterface
340
     */
341 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...
342
        $parentIndex,
343
        string $relationshipName,
344
        $childIndex,
345
        array $attributes,
346
        array $toMany,
347
        string $childApiClass,
348
        ContainerInterface $container,
349
        ServerRequestInterface $request
350
    ): ResponseInterface {
351 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...
352 2
            $parentIndex,
353 2
            $relationshipName,
354 2
            $childIndex,
355 2
            $attributes,
356 2
            $toMany,
357 2
            $childApiClass,
358 2
            $container
359
        );
360
361 2
        $responses = static::createResponses($container, $request);
362 2
        if ($updated > 0) {
363 1
            $modelData = $childApi->read($childIndex)->getData();
364 1
            $response  = $responses->getContentResponse($modelData);
365
        } else {
366 1
            $response = $responses->getCodeResponse(404);
367
        }
368
369 2
        return $response;
370
    }
371
372
    /**
373
     * @param ContainerInterface $container
374
     *
375
     * @return JsonApiValidatorInterface
376
     */
377 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...
378
    {
379 1
        assert(
380 1
            empty(static::ON_CREATE_VALIDATION_RULES_SET_CLASS) === false,
381 1
            'Validation rules set should be defined for class ' . static::class . '.'
382
        );
383
384 1
        return static::createJsonApiValidator($container, static::ON_CREATE_VALIDATION_RULES_SET_CLASS);
385
    }
386
387
    /**
388
     * @param ContainerInterface $container
389
     *
390
     * @return JsonApiValidatorInterface
391
     */
392 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...
393
    {
394 2
        assert(
395 2
            empty(static::ON_UPDATE_VALIDATION_RULES_SET_CLASS) === false,
396 2
            'Validation rules set should be defined for class ' . static::class . '.'
397
        );
398
399 2
        return static::createJsonApiValidator($container, static::ON_UPDATE_VALIDATION_RULES_SET_CLASS);
400
    }
401
402
    /**
403
     * @param ContainerInterface $container
404
     * @param string             $rulesSetClass
405
     *
406
     * @return JsonApiValidatorInterface
407
     */
408 5
    protected static function createJsonApiValidator(
409
        ContainerInterface $container,
410
        string $rulesSetClass
411
    ): JsonApiValidatorInterface {
412
        /** @var JsonApiValidatorFactoryInterface $validatorFactory */
413 5
        $validatorFactory = $container->get(JsonApiValidatorFactoryInterface::class);
414 5
        $validator        = $validatorFactory->createValidator($rulesSetClass);
415
416 5
        return $validator;
417
    }
418
419
    /**
420
     * @param ContainerInterface $container
421
     *
422
     * @return QueryParserInterface
423
     */
424 16
    protected static function createQueryParser(ContainerInterface $container): QueryParserInterface
425
    {
426 16
        return $container->get(QueryParserInterface::class);
427
    }
428
429
    /**
430
     * @param QueryParserInterface $parser
431
     *
432
     * @return QueryParserInterface
433
     */
434 12
    protected static function configureOnIndexParser(QueryParserInterface $parser): QueryParserInterface
435
    {
436 12
        return $parser;
437
    }
438
439
    /**
440
     * @param QueryParserInterface $parser
441
     *
442
     * @return QueryParserInterface
443
     */
444 1
    protected static function configureOnReadParser(QueryParserInterface $parser): QueryParserInterface
445
    {
446 1
        return $parser;
447
    }
448
449
    /**
450
     * @param string               $name
451
     * @param QueryParserInterface $parser
452
     *
453
     * @return QueryParserInterface
454
     *
455
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
456
     */
457 2
    protected static function configureOnReadRelationshipParser(/** @noinspection PhpUnusedParameterInspection */
458
        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...
459
        QueryParserInterface $parser
460
    ): QueryParserInterface {
461 2
        return $parser;
462
    }
463
464
    /**
465
     * @param string               $name
466
     * @param QueryParserInterface $parser
467
     *
468
     * @return QueryParserInterface
469
     *
470
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
471
     */
472 1
    protected static function configureOnReadRelationshipIdentifiersParser(
473
        /** @noinspection PhpUnusedParameterInspection */
474
        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...
475
        QueryParserInterface $parser
476
    ): QueryParserInterface {
477 1
        return $parser;
478
    }
479
480
    /**
481
     * @param ContainerInterface $container
482
     *
483
     * @return ParametersMapperInterface
484
     */
485 15
    protected static function createParameterMapper(ContainerInterface $container): ParametersMapperInterface
486
    {
487
        /** @var SchemaInterface $schemaClass */
488 15
        $schemaClass = static::SCHEMA_CLASS;
489
490
        /** @var ParametersMapperInterface $mapper */
491 15
        $mapper = $container->get(ParametersMapperInterface::class);
492 15
        $mapper->selectRootSchemeByResourceType($schemaClass::TYPE);
493
494 15
        return $mapper;
495
    }
496
497
    /**
498
     * @param ContainerInterface $container
499
     * @param array              $captures
500
     * @param string             $schemeClass
501
     *
502
     * @return array
503
     */
504 4
    protected static function mapSchemeDataToModelData(
505
        ContainerInterface $container,
506
        array $captures,
507
        string $schemeClass
508
    ): array {
509 4
        assert(in_array(SchemaInterface::class, class_implements($schemeClass)));
510
        /** @var SchemaInterface $schemeClass */
511
512 4
        $modelClass = $schemeClass::MODEL;
513 4
        assert(in_array(ModelInterface::class, class_implements($modelClass)));
514
        /** @var ModelInterface $modelClass */
515
516
        /** @var ModelSchemeInfoInterface $schemeInfo */
517 4
        $schemeInfo = $container->get(ModelSchemeInfoInterface::class);
518
519 4
        $index         = null;
520 4
        $fields        = [];
521 4
        $toManyIndexes = [];
522 4
        foreach ($captures as $name => $value) {
523 4
            if ($name === DI::KEYWORD_ID) {
524 4
                $index = $value;
525 4
            } elseif ($schemeClass::hasAttributeMapping($name) === true) {
526 4
                $fieldName          = $schemeClass::getAttributeMapping($name);
527 4
                $fields[$fieldName] = $value;
528 4
            } elseif ($schemeClass::hasRelationshipMapping($name) === true) {
529 2
                $modelRelName = $schemeClass::getRelationshipMapping($name);
530 2
                $relType      = $schemeInfo->getRelationshipType($modelClass, $modelRelName);
531 2
                if ($relType === RelationshipTypes::BELONGS_TO) {
532 2
                    $fkName          = $schemeInfo->getForeignKey($modelClass, $modelRelName);
533 2
                    $fields[$fkName] = $value;
534 2
                } elseif ($relType === RelationshipTypes::BELONGS_TO_MANY) {
535 4
                    $toManyIndexes[$modelRelName] = $value;
536
                }
537
            }
538
        }
539
540 4
        $result = [$index, $fields, $toManyIndexes];
541
542 4
        return $result;
543
    }
544
545
    /**
546
     * @param string               $index
547
     * @param string               $relationshipName
548
     * @param ContainerInterface   $container
549
     * @param QueryParserInterface $parser
550
     *
551
     * @return PaginatedDataInterface
552
     */
553 3
    private static function readRelationshipData(
554
        string $index,
555
        string $relationshipName,
556
        ContainerInterface $container,
557
        QueryParserInterface $parser
558
    ): PaginatedDataInterface {
559 3
        $mapper = static::createParameterMapper($container);
560 3
        $api    = static::createApi($container);
561
562
        $relData = $mapper
563 3
            ->applyQueryParameters($parser, $api)
564 3
            ->readRelationship($index, $relationshipName);
565
566 3
        return $relData;
567
    }
568
569
    /**
570
     * @param ContainerInterface     $container
571
     * @param ServerRequestInterface $request
572
     *
573
     * @return array
574
     */
575 1
    protected static function createImpl(
576
        ContainerInterface $container,
577
        ServerRequestInterface $request
578
    ): array {
579 1
        $jsonData  = static::readJsonFromRequest($container, $request);
580 1
        $validator = static::createOnCreateValidator($container);
581 1
        $captures  = $validator->assert($jsonData)->getJsonApiCaptures();
582
583
        list ($index, $attributes, $toMany) =
584 1
            static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS);
585
586 1
        $api   = static::createApi($container);
587 1
        $index = $api->create($index, $attributes, $toMany);
588
589 1
        return [$index, $api];
590
    }
591
592
    /**
593
     * @param array                  $routeParams
594
     * @param ContainerInterface     $container
595
     * @param ServerRequestInterface $request
596
     *
597
     * @return array [int $updated, string $index, CrudInterface $api]
598
     */
599 4
    protected static function updateImpl(
600
        array $routeParams,
601
        ContainerInterface $container,
602
        ServerRequestInterface $request
603
    ): array {
604 4
        $jsonData  = static::normalizeIndexValueOnUpdate(
605 4
            $routeParams,
606 4
            $container,
607 4
            static::readJsonFromRequest($container, $request)
608
        );
609 2
        $validator = static::createOnUpdateValidator($container);
610 2
        $captures  = $validator->assert($jsonData)->getJsonApiCaptures();
611
612
        list ($index, $attributes, $toMany) =
613 1
            static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS);
614 1
        $api = static::createApi($container);
615
616 1
        $updated = $api->update($index, $attributes, $toMany);
617
618 1
        return [$updated, $index, $api];
619
    }
620
621
    /**
622
     * @param                        $parentIndex
623
     * @param string                 $relationshipName
624
     * @param                        $childIndex
625
     * @param array                  $attributes
626
     * @param array                  $toMany
627
     * @param string                 $childApiClass
628
     * @param ContainerInterface     $container
629
     *
630
     * @return array
631
     */
632 2
    private static function updateInRelationshipImpl(
633
        $parentIndex,
634
        string $relationshipName,
635
        $childIndex,
636
        array $attributes,
637
        array $toMany,
638
        string $childApiClass,
639
        ContainerInterface $container
640
    ): array {
641
        /** @var SchemaInterface $schemaClass */
642 2
        $schemaClass  = static::SCHEMA_CLASS;
643 2
        $modelRelName = $schemaClass::getRelationshipMapping($relationshipName);
644 2
        $hasChild     = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex);
645 2
        if ($hasChild === false) {
646 1
            return [0, null];
647
        }
648
649 1
        $childApi = static::createApi($container, $childApiClass);
650
651 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...
652
653 1
        return [$updated, $childApi];
654
    }
655
656
    /**
657
     * @param ContainerInterface $container
658
     * @param string             $namespace
659
     *
660
     * @return FormatterInterface
661
     */
662 2
    protected static function createMessageFormatter(
663
        ContainerInterface $container,
664
        string $namespace = Messages::RESOURCES_NAMESPACE
665
    ): FormatterInterface {
666
        /** @var FormatterFactoryInterface $factory */
667 2
        $factory          = $container->get(FormatterFactoryInterface::class);
668 2
        $messageFormatter = $factory->createFormatter($namespace);
669
670 2
        return $messageFormatter;
671
    }
672
}
673