Completed
Push — develop ( 9148fa...0fd0fb )
by Neomerx
01:54
created

BaseController::configureOnIndexParser()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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