Completed
Push — master ( 27d944...c3a093 )
by Neomerx
02:52
created

BaseController::updateImpl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 11
cts 11
cp 1
rs 9.3142
c 0
b 0
f 0
cc 1
eloc 16
nc 1
nop 3
crap 1
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 Closure;
20
use Limoncello\Contracts\Application\ModelInterface;
21
use Limoncello\Contracts\Data\ModelSchemeInfoInterface;
22
use Limoncello\Contracts\Data\RelationshipTypes;
23
use Limoncello\Contracts\L10n\FormatterFactoryInterface;
24
use Limoncello\Contracts\L10n\FormatterInterface;
25
use Limoncello\Flute\Contracts\Api\CrudInterface;
26
use Limoncello\Flute\Contracts\FactoryInterface;
27
use Limoncello\Flute\Contracts\Http\ControllerInterface;
28
use Limoncello\Flute\Contracts\Http\Query\ParametersMapperInterface;
29
use Limoncello\Flute\Contracts\Http\Query\QueryParserInterface;
30
use Limoncello\Flute\Contracts\Models\PaginatedDataInterface;
31
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
32
use Limoncello\Flute\Contracts\Validation\JsonApiValidatorFactoryInterface;
33
use Limoncello\Flute\Contracts\Validation\JsonApiValidatorInterface;
34
use Limoncello\Flute\Http\Traits\CreateResponsesTrait;
35
use Limoncello\Flute\L10n\Messages;
36
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as DI;
37
use Neomerx\JsonApi\Exceptions\JsonApiException;
38
use Psr\Container\ContainerExceptionInterface;
39
use Psr\Container\ContainerInterface;
40
use Psr\Container\NotFoundExceptionInterface;
41
use Psr\Http\Message\ResponseInterface;
42
use Psr\Http\Message\ServerRequestInterface;
43
44
/**
45
 * @package Limoncello\Flute
46
 *
47
 * @SuppressWarnings(PHPMD.TooManyMethods)
48
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
49
 */
50
abstract class BaseController implements ControllerInterface
51
{
52
    use CreateResponsesTrait;
53
54
    /** API class name */
55
    const API_CLASS = null;
56
57
    /** JSON API Schema class name */
58
    const SCHEMA_CLASS = null;
59
60
    /** JSON API validation rules set class */
61
    const ON_CREATE_VALIDATION_RULES_SET_CLASS = null;
62
63
    /** JSON API validation rules set class */
64
    const ON_UPDATE_VALIDATION_RULES_SET_CLASS = null;
65
66
    /**
67
     * @inheritdoc
68
     */
69 12
    public static function index(
70
        array $routeParams,
71
        ContainerInterface $container,
72
        ServerRequestInterface $request
73
    ): ResponseInterface {
74
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
75 12
        $parser = static::configureOnIndexParser(
76 12
            static::createQueryParser($container)
77 12
        )->parse($request->getQueryParams());
78 11
        $mapper = static::createParameterMapper($container);
79 11
        $api    = static::createApi($container);
80
81 11
        $models = $mapper->applyQueryParameters($parser, $api)->index();
82
83 11
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
84 11
        $response  = ($models->getData()) === null ?
85 11
            $responses->getCodeResponse(404) : $responses->getContentResponse($models);
86
87 11
        return $response;
88
    }
89
90
    /**
91
     * @inheritdoc
92
     */
93 1
    public static function create(
94
        array $routeParams,
95
        ContainerInterface $container,
96
        ServerRequestInterface $request
97
    ): ResponseInterface {
98 1
        list ($index, $api) = static::createImpl($container, $request);
99
100 1
        $data = $api->read($index);
101 1
        assert(!($data instanceof PaginatedDataInterface));
102
103 1
        $response = static::createResponses($container, $request)->getCreatedResponse($data);
104
105 1
        return $response;
106
    }
107
108
    /**
109
     * @inheritdoc
110
     */
111 1
    public static function read(
112
        array $routeParams,
113
        ContainerInterface $container,
114
        ServerRequestInterface $request
115
    ): ResponseInterface {
116
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
117 1
        $parser = static::configureOnReadParser(
118 1
            static::createQueryParser($container)
119 1
        )->parse($request->getQueryParams());
120 1
        $mapper = static::createParameterMapper($container);
121
122 1
        $index     = $routeParams[static::ROUTE_KEY_INDEX];
123 1
        $modelData = $mapper->applyQueryParameters($parser, static::createApi($container))->read($index);
124 1
        assert(!($modelData instanceof PaginatedDataInterface));
125
126 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
127 1
        $response  = $modelData === null ?
128 1
            $responses->getCodeResponse(404) : $responses->getContentResponse($modelData);
129
130 1
        return $response;
131
    }
132
133
    /**
134
     * @inheritdoc
135
     */
136 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...
137
        array $routeParams,
138
        ContainerInterface $container,
139
        ServerRequestInterface $request
140
    ): ResponseInterface {
141 5
        list ($updated, $index, $api) = static::updateImpl($routeParams, $container, $request);
142
143 2
        $responses = static::createResponses($container, $request);
144 2
        if ($updated > 0) {
145 1
            $modelData = $api->read($index);
146 1
            assert(!($modelData instanceof PaginatedDataInterface));
147
148 1
            return $responses->getContentResponse($modelData);
149
        }
150
151 1
        return $responses->getCodeResponse(404);
152
    }
153
154
    /**
155
     * @inheritdoc
156
     */
157 1
    public static function delete(
158
        array $routeParams,
159
        ContainerInterface $container,
160
        ServerRequestInterface $request
161
    ): ResponseInterface {
162 1
        static::createApi($container)->remove($routeParams[static::ROUTE_KEY_INDEX]);
163
164 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
165
166 1
        return $response;
167
    }
168
169
    /** @deprecated Use `readRelationshipWithClosure` instead
170
     * @param string                 $index
171
     * @param string                 $relationshipName
172
     * @param ContainerInterface     $container
173
     * @param ServerRequestInterface $request
174
     *
175
     * @return ResponseInterface
176
     *
177
     * @throws ContainerExceptionInterface
178
     * @throws NotFoundExceptionInterface
179
     */
180 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...
181
        string $index,
182
        string $relationshipName,
183
        ContainerInterface $container,
184
        ServerRequestInterface $request
185
    ): ResponseInterface {
186 2
        $apiHandler = function (CrudInterface $crud) use ($index, $relationshipName) {
187 2
            return $crud->readRelationship($index, $relationshipName);
188 2
        };
189
190 2
        return static::readRelationshipWithClosure($apiHandler, $relationshipName, $container, $request);
191
    }
192
193
    /** @deprecated Use `readRelationshipIdentifiersWithClosure` instead
194
     * @param string                 $index
195
     * @param string                 $relationshipName
196
     * @param ContainerInterface     $container
197
     * @param ServerRequestInterface $request
198
     *
199
     * @return ResponseInterface
200
     *
201
     * @throws ContainerExceptionInterface
202
     * @throws NotFoundExceptionInterface
203
     */
204 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...
205
        string $index,
206
        string $relationshipName,
207
        ContainerInterface $container,
208
        ServerRequestInterface $request
209
    ): ResponseInterface {
210 1
        $apiHandler = function (CrudInterface $crud) use ($index, $relationshipName) {
211 1
            return $crud->readRelationship($index, $relationshipName);
212 1
        };
213
214 1
        return static::readRelationshipIdentifiersWithClosure($apiHandler, $relationshipName, $container, $request);
215
    }
216
217
    /**
218
     * @param Closure                $apiHandler
219
     * @param string                 $relationshipName
220
     * @param ContainerInterface     $container
221
     * @param ServerRequestInterface $request
222
     *
223
     * @return ResponseInterface
224
     *
225
     * @throws ContainerExceptionInterface
226
     * @throws NotFoundExceptionInterface
227
     */
228 2 View Code Duplication
    protected static function readRelationshipWithClosure(
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...
229
        Closure $apiHandler,
230
        string $relationshipName,
231
        ContainerInterface $container,
232
        ServerRequestInterface $request
233
    ): ResponseInterface {
234
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
235 2
        $parser = static::configureOnReadRelationshipParser(
236 2
            $relationshipName,
237 2
            static::createQueryParser($container)
238 2
        )->parse($request->getQueryParams());
239
240 2
        $mapper  = static::createParameterMapper($container);
241 2
        $api     = $mapper->applyQueryParameters($parser, static::createApi($container));
242 2
        $relData = call_user_func($apiHandler, $api, $container);
243
244 2
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
245 2
        $response  = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null) ?
246 2
            $responses->getCodeResponse(404) : $responses->getContentResponse($relData);
247
248 2
        return $response;
249
    }
250
251
    /**
252
     * @param Closure                $apiHandler
253
     * @param string                 $relationshipName
254
     * @param ContainerInterface     $container
255
     * @param ServerRequestInterface $request
256
     *
257
     * @return ResponseInterface
258
     *
259
     * @throws ContainerExceptionInterface
260
     * @throws NotFoundExceptionInterface
261
     */
262 1 View Code Duplication
    protected static function readRelationshipIdentifiersWithClosure(
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...
263
        Closure $apiHandler,
264
        string $relationshipName,
265
        ContainerInterface $container,
266
        ServerRequestInterface $request
267
    ): ResponseInterface {
268
        // By default no filters, sorts or includes are allowed from query. You can override this method to change it.
269 1
        $parser = static::configureOnReadRelationshipIdentifiersParser(
270 1
            $relationshipName,
271 1
            static::createQueryParser($container)
272 1
        )->parse($request->getQueryParams());
273
274 1
        $mapper  = static::createParameterMapper($container);
275 1
        $api     = $mapper->applyQueryParameters($parser, static::createApi($container));
276 1
        $relData = call_user_func($apiHandler, $api, $container);
277
278 1
        $responses = static::createResponses($container, $request, $parser->createEncodingParameters());
279 1
        $response  = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null) ?
280 1
            $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData);
281
282 1
        return $response;
283
    }
284
285
    /**
286
     * @param ContainerInterface $container
287
     * @param string|null        $class
288
     *
289
     * @return CrudInterface
290
     *
291
     * @throws ContainerExceptionInterface
292
     * @throws NotFoundExceptionInterface
293
     */
294 22
    protected static function createApi(ContainerInterface $container, string $class = null): CrudInterface
295
    {
296
        /** @var FactoryInterface $factory */
297 22
        $factory = $container->get(FactoryInterface::class);
298 22
        $api     = $factory->createApi($class ?? static::API_CLASS);
299
300 22
        return $api;
301
    }
302
303
    /**
304
     * @param ContainerInterface     $container
305
     * @param ServerRequestInterface $request
306
     *
307
     * @return array
308
     *
309
     * @throws ContainerExceptionInterface
310
     * @throws NotFoundExceptionInterface
311
     */
312 8
    protected static function readJsonFromRequest(ContainerInterface $container, ServerRequestInterface $request): array
313
    {
314 8
        $body = (string)$request->getBody();
315 8
        if (empty($body) === true || ($json = json_decode($body, true)) === null) {
316
            /** @var FactoryInterface $factory */
317 1
            $factory = $container->get(FactoryInterface::class);
318 1
            $errors  = $factory->createErrorCollection();
319 1
            $errors->addDataError(
320 1
                static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
321
            );
322
323 1
            throw new JsonApiException($errors);
324
        }
325
326 7
        return $json;
327
    }
328
329
    /**
330
     * @param array              $routeParams
331
     * @param ContainerInterface $container
332
     * @param array              $jsonData
333
     *
334
     * @return array
335
     * @SuppressWarnings(PHPMD.ElseExpression)
336
     *
337
     * @throws ContainerExceptionInterface
338
     * @throws NotFoundExceptionInterface
339
     */
340 4
    protected static function normalizeIndexValueOnUpdate(
341
        array $routeParams,
342
        ContainerInterface $container,
343
        array $jsonData
344
    ): array {
345
        // check that index in data and URL are identical
346 4
        $index         = $routeParams[static::ROUTE_KEY_INDEX];
347 4
        $dataSection   = null;
348
        $hasIndexValue =
349 4
            array_key_exists(DI::KEYWORD_DATA, $jsonData) &&
350 4
            array_key_exists(DI::KEYWORD_ID, ($dataSection = $jsonData[DI::KEYWORD_DATA]));
351 4
        if ($hasIndexValue === true) {
352 3
            assert($dataSection !== null);
353 3
            if ($dataSection[DI::KEYWORD_ID] !== $index) {
354
                /** @var FactoryInterface $factory */
355 1
                $factory = $container->get(FactoryInterface::class);
356 1
                $errors  = $factory->createErrorCollection();
357 1
                $errors->addDataIdError(
358 1
                    static::createMessageFormatter($container)->formatMessage(Messages::MSG_ERR_INVALID_ELEMENT)
359
                );
360
361 3
                throw new JsonApiException($errors);
362
            }
363
        } else {
364
            // put the index to data for our convenience
365 1
            $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] = $index;
366
        }
367
368 3
        return $jsonData;
369
    }
370
371
    /**
372
     * @param int|string             $parentIndex
373
     * @param string                 $relationshipName
374
     * @param int|string             $childIndex
375
     * @param string                 $childApiClass
376
     * @param ContainerInterface     $container
377
     * @param ServerRequestInterface $request
378
     *
379
     * @return ResponseInterface
380
     *
381
     * @throws ContainerExceptionInterface
382
     * @throws NotFoundExceptionInterface
383
     */
384 1
    protected static function deleteInRelationship(
385
        $parentIndex,
386
        string $relationshipName,
387
        $childIndex,
388
        string $childApiClass,
389
        ContainerInterface $container,
390
        ServerRequestInterface $request
391
    ): ResponseInterface {
392
        /** @var SchemaInterface $schemaClass */
393 1
        $schemaClass  = static::SCHEMA_CLASS;
394 1
        $modelRelName = $schemaClass::getRelationshipMapping($relationshipName);
395 1
        $hasChild     = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex);
396 1
        if ($hasChild === false) {
397 1
            return static::createResponses($container, $request)->getCodeResponse(404);
398
        }
399
400 1
        $childApi = static::createApi($container, $childApiClass);
401
402 1
        $childApi->remove($childIndex);
403 1
        $response = static::createResponses($container, $request)->getCodeResponse(204);
404
405 1
        return $response;
406
    }
407
408
    /** @noinspection PhpTooManyParametersInspection
409
     * @param int|string             $parentIndex
410
     * @param string                 $relationshipName
411
     * @param int|string             $childIndex
412
     * @param array                  $attributes
413
     * @param array                  $toMany
414
     * @param string                 $childApiClass
415
     * @param ContainerInterface     $container
416
     * @param ServerRequestInterface $request
417
     *
418
     * @return ResponseInterface
419
     *
420
     * @throws ContainerExceptionInterface
421
     * @throws NotFoundExceptionInterface
422
     */
423 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...
424
        $parentIndex,
425
        string $relationshipName,
426
        $childIndex,
427
        array $attributes,
428
        array $toMany,
429
        string $childApiClass,
430
        ContainerInterface $container,
431
        ServerRequestInterface $request
432
    ): ResponseInterface {
433
        /** @var CrudInterface $childApi */
434 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...
435 2
            $parentIndex,
436 2
            $relationshipName,
437 2
            $childIndex,
438 2
            $attributes,
439 2
            $toMany,
440 2
            $childApiClass,
441 2
            $container
442
        );
443
444 2
        $responses = static::createResponses($container, $request);
445 2
        if ($updated > 0) {
446 1
            $modelData = $childApi->read($childIndex);
447 1
            assert(!($modelData instanceof PaginatedDataInterface));
448
449 1
            return $responses->getContentResponse($modelData);
450
        }
451
452 1
        return $responses->getCodeResponse(404);
453
    }
454
455
    /**
456
     * @param ContainerInterface $container
457
     *
458
     * @return JsonApiValidatorInterface
459
     *
460
     * @throws ContainerExceptionInterface
461
     * @throws NotFoundExceptionInterface
462
     */
463 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...
464
    {
465 1
        assert(
466 1
            empty(static::ON_CREATE_VALIDATION_RULES_SET_CLASS) === false,
467 1
            'Validation rules set should be defined for class ' . static::class . '.'
468
        );
469
470 1
        return static::createJsonApiValidator($container, static::ON_CREATE_VALIDATION_RULES_SET_CLASS);
471
    }
472
473
    /**
474
     * @param ContainerInterface $container
475
     *
476
     * @return JsonApiValidatorInterface
477
     *
478
     * @throws ContainerExceptionInterface
479
     * @throws NotFoundExceptionInterface
480
     */
481 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...
482
    {
483 3
        assert(
484 3
            empty(static::ON_UPDATE_VALIDATION_RULES_SET_CLASS) === false,
485 3
            'Validation rules set should be defined for class ' . static::class . '.'
486
        );
487
488 3
        return static::createJsonApiValidator($container, static::ON_UPDATE_VALIDATION_RULES_SET_CLASS);
489
    }
490
491
    /**
492
     * @param ContainerInterface $container
493
     * @param string             $rulesSetClass
494
     *
495
     * @return JsonApiValidatorInterface
496
     *
497
     * @throws ContainerExceptionInterface
498
     * @throws NotFoundExceptionInterface
499
     */
500 6
    protected static function createJsonApiValidator(
501
        ContainerInterface $container,
502
        string $rulesSetClass
503
    ): JsonApiValidatorInterface {
504
        /** @var JsonApiValidatorFactoryInterface $validatorFactory */
505 6
        $validatorFactory = $container->get(JsonApiValidatorFactoryInterface::class);
506 6
        $validator        = $validatorFactory->createValidator($rulesSetClass);
507
508 6
        return $validator;
509
    }
510
511
    /**
512
     * @param ContainerInterface $container
513
     *
514
     * @return QueryParserInterface
515
     *
516
     * @throws ContainerExceptionInterface
517
     * @throws NotFoundExceptionInterface
518
     */
519 16
    protected static function createQueryParser(ContainerInterface $container): QueryParserInterface
520
    {
521 16
        return $container->get(QueryParserInterface::class);
522
    }
523
524
    /**
525
     * @param QueryParserInterface $parser
526
     *
527
     * @return QueryParserInterface
528
     */
529 12
    protected static function configureOnIndexParser(QueryParserInterface $parser): QueryParserInterface
530
    {
531 12
        return $parser;
532
    }
533
534
    /**
535
     * @param QueryParserInterface $parser
536
     *
537
     * @return QueryParserInterface
538
     */
539 1
    protected static function configureOnReadParser(QueryParserInterface $parser): QueryParserInterface
540
    {
541 1
        return $parser;
542
    }
543
544
    /**
545
     * @param string               $name
546
     * @param QueryParserInterface $parser
547
     *
548
     * @return QueryParserInterface
549
     *
550
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
551
     */
552 2
    protected static function configureOnReadRelationshipParser(
553
        /** @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...
554
        QueryParserInterface $parser
555
    ): QueryParserInterface {
556 2
        return $parser;
557
    }
558
559
    /**
560
     * @param string               $name
561
     * @param QueryParserInterface $parser
562
     *
563
     * @return QueryParserInterface
564
     *
565
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
566
     */
567 1
    protected static function configureOnReadRelationshipIdentifiersParser(
568
        /** @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...
569
        QueryParserInterface $parser
570
    ): QueryParserInterface {
571 1
        return $parser;
572
    }
573
574
    /**
575
     * @param ContainerInterface $container
576
     *
577
     * @return ParametersMapperInterface
578
     *
579
     * @throws ContainerExceptionInterface
580
     * @throws NotFoundExceptionInterface
581
     */
582 15
    protected static function createParameterMapper(ContainerInterface $container): ParametersMapperInterface
583
    {
584
        /** @var SchemaInterface $schemaClass */
585 15
        $schemaClass = static::SCHEMA_CLASS;
586
587
        /** @var ParametersMapperInterface $mapper */
588 15
        $mapper = $container->get(ParametersMapperInterface::class);
589 15
        $mapper->selectRootSchemeByResourceType($schemaClass::TYPE);
590
591 15
        return $mapper;
592
    }
593
594
    /**
595
     * @param ContainerInterface $container
596
     * @param array              $captures
597
     * @param string             $schemeClass
598
     *
599
     * @return array
600
     *
601
     * @throws ContainerExceptionInterface
602
     * @throws NotFoundExceptionInterface
603
     */
604 5
    protected static function mapSchemeDataToModelData(
605
        ContainerInterface $container,
606
        array $captures,
607
        string $schemeClass
608
    ): array {
609 5
        assert(in_array(SchemaInterface::class, class_implements($schemeClass)));
610
        /** @var SchemaInterface $schemeClass */
611
612 5
        $modelClass = $schemeClass::MODEL;
613 5
        assert(in_array(ModelInterface::class, class_implements($modelClass)));
614
        /** @var ModelInterface $modelClass */
615
616
        /** @var ModelSchemeInfoInterface $schemeInfo */
617 5
        $schemeInfo = $container->get(ModelSchemeInfoInterface::class);
618
619 5
        $index         = null;
620 5
        $fields        = [];
621 5
        $toManyIndexes = [];
622 5
        foreach ($captures as $name => $value) {
623 5
            if ($name === DI::KEYWORD_ID) {
624 5
                $index = $value;
625 5
            } elseif ($schemeClass::hasAttributeMapping($name) === true) {
626 5
                $fieldName          = $schemeClass::getAttributeMapping($name);
627 5
                $fields[$fieldName] = $value;
628 5
            } elseif ($schemeClass::hasRelationshipMapping($name) === true) {
629 2
                $modelRelName = $schemeClass::getRelationshipMapping($name);
630 2
                $relType      = $schemeInfo->getRelationshipType($modelClass, $modelRelName);
631 2
                if ($relType === RelationshipTypes::BELONGS_TO) {
632 2
                    $fkName          = $schemeInfo->getForeignKey($modelClass, $modelRelName);
633 2
                    $fields[$fkName] = $value;
634 2
                } elseif ($relType === RelationshipTypes::BELONGS_TO_MANY) {
635 5
                    $toManyIndexes[$modelRelName] = $value;
636
                }
637
            }
638
        }
639
640 5
        $result = [$index, $fields, $toManyIndexes];
641
642 5
        return $result;
643
    }
644
645
    /**
646
     * @param ContainerInterface     $container
647
     * @param ServerRequestInterface $request
648
     *
649
     * @return array
650
     *
651
     * @throws ContainerExceptionInterface
652
     * @throws NotFoundExceptionInterface
653
     */
654 1
    protected static function createImpl(
655
        ContainerInterface $container,
656
        ServerRequestInterface $request
657
    ): array {
658 1
        $jsonData  = static::readJsonFromRequest($container, $request);
659 1
        $validator = static::createOnCreateValidator($container);
660 1
        $captures  = $validator->assert($jsonData)->getJsonApiCaptures();
661
662
        list ($index, $attributes, $toMany) =
663 1
            static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS);
664
665 1
        $api   = static::createApi($container);
666 1
        $index = $api->create($index, $attributes, $toMany);
667
668 1
        return [$index, $api];
669
    }
670
671
    /**
672
     * @param array                  $routeParams
673
     * @param ContainerInterface     $container
674
     * @param ServerRequestInterface $request
675
     *
676
     * @return array [int $updated, string $index, CrudInterface $api]
677
     *
678
     * @throws ContainerExceptionInterface
679
     * @throws NotFoundExceptionInterface
680
     */
681 5
    protected static function updateImpl(
682
        array $routeParams,
683
        ContainerInterface $container,
684
        ServerRequestInterface $request
685
    ): array {
686 5
        $jsonData  = static::normalizeIndexValueOnUpdate(
687 5
            $routeParams,
688 5
            $container,
689 5
            static::readJsonFromRequest($container, $request)
690
        );
691 3
        $validator = static::createOnUpdateValidator($container);
692 3
        $captures  = $validator->assert($jsonData)->getJsonApiCaptures();
693
694
        list ($index, $attributes, $toMany) =
695 2
            static::mapSchemeDataToModelData($container, $captures, static::SCHEMA_CLASS);
696 2
        $api = static::createApi($container);
697
698 2
        $updated = $api->update($index, $attributes, $toMany);
699
700 2
        return [$updated, $index, $api];
701
    }
702
703
    /**
704
     * @param                        $parentIndex
705
     * @param string                 $relationshipName
706
     * @param                        $childIndex
707
     * @param array                  $attributes
708
     * @param array                  $toMany
709
     * @param string                 $childApiClass
710
     * @param ContainerInterface     $container
711
     *
712
     * @return array
713
     *
714
     * @throws ContainerExceptionInterface
715
     * @throws NotFoundExceptionInterface
716
     */
717 2
    private static function updateInRelationshipImpl(
718
        $parentIndex,
719
        string $relationshipName,
720
        $childIndex,
721
        array $attributes,
722
        array $toMany,
723
        string $childApiClass,
724
        ContainerInterface $container
725
    ): array {
726
        /** @var SchemaInterface $schemaClass */
727 2
        $schemaClass  = static::SCHEMA_CLASS;
728 2
        $modelRelName = $schemaClass::getRelationshipMapping($relationshipName);
729 2
        $hasChild     = static::createApi($container)->hasInRelationship($parentIndex, $modelRelName, $childIndex);
730 2
        if ($hasChild === false) {
731 1
            return [0, null];
732
        }
733
734 1
        $childApi = static::createApi($container, $childApiClass);
735
736 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...
737
738 1
        return [$updated, $childApi];
739
    }
740
741
    /**
742
     * @param ContainerInterface $container
743
     * @param string             $namespace
744
     *
745
     * @return FormatterInterface
746
     *
747
     * @throws ContainerExceptionInterface
748
     * @throws NotFoundExceptionInterface
749
     */
750 2
    protected static function createMessageFormatter(
751
        ContainerInterface $container,
752
        string $namespace = Messages::RESOURCES_NAMESPACE
753
    ): FormatterInterface {
754
        /** @var FormatterFactoryInterface $factory */
755 2
        $factory          = $container->get(FormatterFactoryInterface::class);
756 2
        $messageFormatter = $factory->createFormatter($namespace);
757
758 2
        return $messageFormatter;
759
    }
760
}
761