Completed
Push — master ( dcfc04...697864 )
by Neomerx
04:30
created

defaultCreateHandler()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 12
cts 12
cp 1
rs 9.44
c 0
b 0
f 0
cc 1
nc 1
nop 12
crap 1

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php namespace Limoncello\Flute\Http\Traits;
2
3
/**
4
 * Copyright 2015-2018 [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 Doctrine\DBAL\Exception\UniqueConstraintViolationException;
21
use Limoncello\Contracts\Application\ModelInterface;
22
use Limoncello\Contracts\Data\ModelSchemaInfoInterface;
23
use Limoncello\Contracts\Data\RelationshipTypes;
24
use Limoncello\Contracts\L10n\FormatterFactoryInterface;
25
use Limoncello\Flute\Contracts\Api\CrudInterface;
26
use Limoncello\Flute\Contracts\Encoder\EncoderInterface;
27
use Limoncello\Flute\Contracts\FactoryInterface;
28
use Limoncello\Flute\Contracts\Http\Query\ParametersMapperInterface;
29
use Limoncello\Flute\Contracts\Models\PaginatedDataInterface;
30
use Limoncello\Flute\Contracts\Schema\JsonSchemasInterface;
31
use Limoncello\Flute\Contracts\Schema\SchemaInterface;
32
use Limoncello\Flute\Contracts\Validation\FormRulesInterface;
33
use Limoncello\Flute\Contracts\Validation\FormValidatorFactoryInterface;
34
use Limoncello\Flute\Contracts\Validation\FormValidatorInterface;
35
use Limoncello\Flute\Contracts\Validation\JsonApiDataParserInterface;
36
use Limoncello\Flute\Contracts\Validation\JsonApiDataRulesInterface;
37
use Limoncello\Flute\Contracts\Validation\JsonApiParserFactoryInterface;
38
use Limoncello\Flute\Contracts\Validation\JsonApiQueryParserInterface;
39
use Limoncello\Flute\Contracts\Validation\JsonApiQueryRulesInterface;
40
use Limoncello\Flute\Http\JsonApiResponse;
41
use Limoncello\Flute\Http\Responses;
42
use Limoncello\Flute\Package\FluteSettings as S;
43
use Limoncello\Flute\Resources\Messages\En\Generic;
44
use Limoncello\Flute\Validation\JsonApi\Rules\DefaultQueryValidationRules;
45
use Neomerx\JsonApi\Contracts\Http\Headers\MediaTypeInterface;
46
use Neomerx\JsonApi\Contracts\Http\ResponsesInterface;
47
use Neomerx\JsonApi\Contracts\Schema\DocumentInterface as DI;
48
use Neomerx\JsonApi\Exceptions\JsonApiException;
49
use Neomerx\JsonApi\Http\Headers\MediaType;
50
use Psr\Container\ContainerExceptionInterface;
51
use Psr\Container\ContainerInterface;
52
use Psr\Container\NotFoundExceptionInterface;
53
use Psr\Http\Message\ResponseInterface;
54
use Psr\Http\Message\UriInterface;
55
56
/**
57
 * @package Limoncello\Flute
58
 */
59
trait DefaultControllerMethodsTrait
60
{
61
    /** @noinspection PhpTooManyParametersInspection
62
     * @param array                       $queryParams
63
     * @param UriInterface                $requestUri
64
     * @param JsonApiQueryParserInterface $queryParser
65
     * @param ParametersMapperInterface   $mapper
66
     * @param CrudInterface               $crud
67
     * @param EncoderInterface            $encoder
68
     *
69
     * @return ResponseInterface
70
     */
71 12
    protected static function defaultIndexHandler(
72
        array $queryParams,
73
        UriInterface $requestUri,
74
        JsonApiQueryParserInterface $queryParser,
75
        ParametersMapperInterface $mapper,
76
        CrudInterface $crud,
77
        EncoderInterface $encoder
78
    ): ResponseInterface {
79 12
        $queryParser->parse(null, $queryParams);
80
81 11
        $models = $mapper->applyQueryParameters($queryParser, $crud)->index();
82
83 10
        self::defaultApplyIncludesAndFieldSetsToEncoder($queryParser, $encoder);
84 10
        $responses = static::defaultCreateResponses($requestUri, $encoder);
85 10
        $response  = ($models->getData()) === null ?
86 10
            $responses->getCodeResponse(404) : $responses->getContentResponse($models);
87
88 10
        return $response;
89
    }
90
91
    /** @noinspection PhpTooManyParametersInspection
92
     * @param string                      $index
93
     * @param array                       $queryParams
94
     * @param UriInterface                $requestUri
95
     * @param JsonApiQueryParserInterface $queryParser
96
     * @param ParametersMapperInterface   $mapper
97
     * @param CrudInterface               $crud
98
     * @param EncoderInterface            $encoder
99
     *
100
     * @return ResponseInterface
101
     */
102 1
    protected static function defaultReadHandler(
103
        string $index,
104
        array $queryParams,
105
        UriInterface $requestUri,
106
        JsonApiQueryParserInterface $queryParser,
107
        ParametersMapperInterface $mapper,
108
        CrudInterface $crud,
109
        EncoderInterface $encoder
110
    ): ResponseInterface {
111 1
        $queryParser->parse($index, $queryParams);
112 1
        $validatedIndex = $queryParser->getIdentity();
113
114 1
        $model = $mapper->applyQueryParameters($queryParser, $crud)->read($validatedIndex);
115 1
        assert(!($model instanceof PaginatedDataInterface));
116
117 1
        self::defaultApplyIncludesAndFieldSetsToEncoder($queryParser, $encoder);
118 1
        $responses = static::defaultCreateResponses($requestUri, $encoder);
119 1
        $response  = $model === null ?
120 1
            $responses->getCodeResponse(404) : $responses->getContentResponse($model);
121
122 1
        return $response;
123
    }
124
125
    /** @noinspection PhpTooManyParametersInspection
126
     * @param string                      $index
127
     * @param Closure                     $apiHandler
128
     * @param array                       $queryParams
129
     * @param UriInterface                $requestUri
130
     * @param JsonApiQueryParserInterface $queryParser
131
     * @param ParametersMapperInterface   $mapper
132
     * @param CrudInterface               $crud
133
     * @param EncoderInterface            $encoder
134
     *
135
     * @return ResponseInterface
136
     *
137
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
138
     */
139 2
    protected static function defaultReadRelationshipWithClosureHandler(
140
        string $index,
141
        Closure $apiHandler,
142
        array $queryParams,
143
        UriInterface $requestUri,
144
        JsonApiQueryParserInterface $queryParser,
145
        ParametersMapperInterface $mapper,
146
        CrudInterface $crud,
147
        EncoderInterface $encoder
148
    ): ResponseInterface {
149 2
        $queryParser->parse($index, $queryParams);
150 2
        $mapper->applyQueryParameters($queryParser, $crud);
151
152 2
        $relData = call_user_func($apiHandler);
153
154 2
        self::defaultApplyIncludesAndFieldSetsToEncoder($queryParser, $encoder);
155 2
        $responses = static::defaultCreateResponses($requestUri, $encoder);
156
157 2
        $noData   = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null);
158 2
        $response = $noData === true ? $responses->getCodeResponse(404) : $responses->getContentResponse($relData);
159
160 2
        return $response;
161
    }
162
163
    /** @noinspection PhpTooManyParametersInspection
164
     * @param string                      $index
165
     * @param Closure                     $apiHandler
166
     * @param array                       $queryParams
167
     * @param UriInterface                $requestUri
168
     * @param JsonApiQueryParserInterface $queryParser
169
     * @param ParametersMapperInterface   $mapper
170
     * @param CrudInterface               $crud
171
     * @param EncoderInterface            $encoder
172
     *
173
     * @return ResponseInterface
174
     *
175
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
176
     */
177 1
    protected static function defaultReadRelationshipIdentifiersWithClosureHandler(
178
        string $index,
179
        Closure $apiHandler,
180
        array $queryParams,
181
        UriInterface $requestUri,
182
        JsonApiQueryParserInterface $queryParser,
183
        ParametersMapperInterface $mapper,
184
        CrudInterface $crud,
185
        EncoderInterface $encoder
186
    ): ResponseInterface {
187 1
        $queryParser->parse($index, $queryParams);
188 1
        $mapper->applyQueryParameters($queryParser, $crud);
189
190 1
        $relData = call_user_func($apiHandler);
191
192 1
        self::defaultApplyIncludesAndFieldSetsToEncoder($queryParser, $encoder);
193 1
        $responses = static::defaultCreateResponses($requestUri, $encoder);
194
195 1
        $noData   = $relData === null || ($relData instanceof PaginatedDataInterface && $relData->getData() === null);
196 1
        $response = $noData === true ? $responses->getCodeResponse(404) : $responses->getIdentifiersResponse($relData);
197
198 1
        return $response;
199
    }
200
201
    /** @noinspection PhpTooManyParametersInspection
202
     * @param UriInterface               $requestUri
203
     * @param string                     $requestBody
204
     * @param string                     $schemaClass
205
     * @param ModelSchemaInfoInterface   $schemaInfo
206
     * @param JsonApiDataParserInterface $parser
207
     * @param CrudInterface              $crud
208
     * @param JsonSchemasInterface       $jsonSchemas
209
     * @param EncoderInterface           $encoder
210
     * @param FactoryInterface           $errorFactory
211
     * @param FormatterFactoryInterface  $formatterFactory
212
     * @param string                     $messagesNamespace
213
     * @param string                     $errorMessage
214
     *
215
     * @return ResponseInterface
216
     *
217
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
218
     */
219 2
    protected static function defaultCreateHandler(
220
        UriInterface $requestUri,
221
        string $requestBody,
222
        string $schemaClass,
223
        ModelSchemaInfoInterface $schemaInfo,
224
        JsonApiDataParserInterface $parser,
225
        CrudInterface $crud,
226
        JsonSchemasInterface $jsonSchemas,
227
        EncoderInterface $encoder,
228
        FactoryInterface $errorFactory,
229
        FormatterFactoryInterface $formatterFactory,
230
        string $messagesNamespace = S::GENERIC_NAMESPACE,
231
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
232
    ): ResponseInterface {
233
        // some of the users want to reuse default `create` but have a custom part for responses
234
        // to meet this requirement it is split into two parts.
235 2
        $index = static::defaultCreate(
236 2
            $requestBody,
237 2
            $schemaClass,
238 2
            $schemaInfo,
239 2
            $parser,
240 2
            $crud,
241 2
            $errorFactory,
242 2
            $formatterFactory,
243 2
            $messagesNamespace,
244 2
            $errorMessage
245
        );
246
247 1
        return static::defaultCreateResponse($index, $requestUri, $crud, $jsonSchemas, $encoder);
248
    }
249
250
    /** @noinspection PhpTooManyParametersInspection
251
     * @param string                     $requestBody
252
     * @param string                     $schemaClass
253
     * @param ModelSchemaInfoInterface   $schemaInfo
254
     * @param JsonApiDataParserInterface $parser
255
     * @param CrudInterface              $crud
256
     * @param FactoryInterface           $errorFactory
257
     * @param FormatterFactoryInterface  $formatterFactory
258
     * @param string                     $messagesNamespace
259
     * @param string                     $errorMessage
260
     *
261
     * @return ResponseInterface
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
262
     */
263 2
    protected static function defaultCreate(
264
        string $requestBody,
265
        string $schemaClass,
266
        ModelSchemaInfoInterface $schemaInfo,
267
        JsonApiDataParserInterface $parser,
268
        CrudInterface $crud,
269
        FactoryInterface $errorFactory,
270
        FormatterFactoryInterface $formatterFactory,
271
        string $messagesNamespace = S::GENERIC_NAMESPACE,
272
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
273
    ): string {
274 2
        $jsonData = static::readJsonFromRequest(
275 2
            $requestBody,
276 2
            $errorFactory,
277 2
            $formatterFactory,
278 2
            $messagesNamespace,
279 2
            $errorMessage
280
        );
281
282 2
        $captures = $parser->assert($jsonData)->getCaptures();
283
284 2
        list ($index, $attributes, $toMany) = static::mapSchemaDataToModelData($captures, $schemaClass, $schemaInfo);
285
286
        try {
287 2
            $index = $crud->create($index, $attributes, $toMany);
288 1
        } /** @noinspection PhpRedundantCatchClauseInspection */ catch (UniqueConstraintViolationException $exception) {
289 1
            $errors    = $errorFactory->createErrorCollection();
290 1
            $formatter = $formatterFactory->createFormatter($messagesNamespace);
291 1
            $title     = $formatter->formatMessage($errorMessage);
292 1
            $details   = null;
293 1
            $errorCode = JsonApiResponse::HTTP_CONFLICT;
294 1
            $errors->addDataError($title, $details, $errorCode);
295
296 1
            throw new JsonApiException($errors);
0 ignored issues
show
Documentation introduced by
$errors is of type object<Neomerx\JsonApi\Schema\ErrorCollection>, but the function expects a object<Neomerx\JsonApi\C...pi\Exceptions\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...
297
        }
298
299 1
        return $index;
300
    }
301
302
    /**
303
     * @param string               $index
304
     * @param UriInterface         $requestUri
305
     * @param CrudInterface        $crud
306
     * @param JsonSchemasInterface $jsonSchemas
307
     * @param EncoderInterface     $encoder
308
     *
309
     * @return ResponseInterface
310
     */
311 1
    protected static function defaultCreateResponse(
312
        string $index,
313
        UriInterface $requestUri,
314
        CrudInterface $crud,
315
        JsonSchemasInterface $jsonSchemas,
316
        EncoderInterface $encoder
317
    ): ResponseInterface {
318 1
        $model = $crud->read($index);
319 1
        assert($model !== null && !($model instanceof PaginatedDataInterface));
320
321 1
        $responses = static::defaultCreateResponses($requestUri, $encoder);
322 1
        $fullUrl   = static::defaultGetResourceUrl($model, $requestUri, $jsonSchemas);
323 1
        $response  = $responses->getCreatedResponse($model, $fullUrl);
324
325 1
        return $response;
326
    }
327
328
    /** @noinspection PhpTooManyParametersInspection
329
     * @param string                     $index
330
     * @param UriInterface               $requestUri
331
     * @param string                     $requestBody
332
     * @param string                     $schemaClass
333
     * @param ModelSchemaInfoInterface   $schemaInfo
334
     * @param JsonApiDataParserInterface $parser
335
     * @param CrudInterface              $crud
336
     * @param EncoderInterface           $encoder
337
     * @param FactoryInterface           $errorFactory
338
     * @param FormatterFactoryInterface  $formatterFactory
339
     * @param string                     $messagesNamespace
340
     * @param string                     $errorMessage
341
     *
342
     * @return ResponseInterface
343
     *
344
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
345
     */
346 6
    protected static function defaultUpdateHandler(
347
        string $index,
348
        UriInterface $requestUri,
349
        string $requestBody,
350
        string $schemaClass,
351
        ModelSchemaInfoInterface $schemaInfo,
352
        JsonApiDataParserInterface $parser,
353
        CrudInterface $crud,
354
        EncoderInterface $encoder,
355
        FactoryInterface $errorFactory,
356
        FormatterFactoryInterface $formatterFactory,
357
        string $messagesNamespace = S::GENERIC_NAMESPACE,
358
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
359
    ): ResponseInterface {
360
        // some of the users want to reuse default `update` but have a custom part for responses
361
        // to meet this requirement it is split into two parts.
362 6
        $updated = static::defaultUpdate(
363 6
            $index,
364 6
            $requestBody,
365 6
            $schemaClass,
366 6
            $schemaInfo,
367 6
            $parser,
368 6
            $crud,
369 6
            $errorFactory,
370 6
            $formatterFactory,
371 6
            $messagesNamespace,
372 6
            $errorMessage
373
        );
374
375 2
        return static::defaultUpdateResponse($updated, $index, $requestUri, $crud, $encoder);
376
    }
377
378
    /** @noinspection PhpTooManyParametersInspection
379
     * @param string                     $index
380
     * @param string                     $requestBody
381
     * @param string                     $schemaClass
382
     * @param ModelSchemaInfoInterface   $schemaInfo
383
     * @param JsonApiDataParserInterface $parser
384
     * @param CrudInterface              $crud
385
     * @param FactoryInterface           $errorFactory
386
     * @param FormatterFactoryInterface  $formatterFactory
387
     * @param string                     $messagesNamespace
388
     * @param string                     $errorMessage
389
     *
390
     * @return int
391
     *
392
     * @SuppressWarnings(PHPMD.ElseExpression)
393
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
394
     */
395 6
    protected static function defaultUpdate(
396
        string $index,
397
        string $requestBody,
398
        string $schemaClass,
399
        ModelSchemaInfoInterface $schemaInfo,
400
        JsonApiDataParserInterface $parser,
401
        CrudInterface $crud,
402
        FactoryInterface $errorFactory,
403
        FormatterFactoryInterface $formatterFactory,
404
        string $messagesNamespace = S::GENERIC_NAMESPACE,
405
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
406
    ): int {
407 6
        $jsonData = static::readJsonFromRequest(
408 6
            $requestBody,
409 6
            $errorFactory,
410 6
            $formatterFactory,
411 6
            $messagesNamespace,
412 6
            $errorMessage
413
        );
414
415
        // check that index in data and URL are identical
416 5
        $indexValue = $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] ?? null;
417 5
        if (empty($indexValue) === false) {
418 4
            if ($indexValue !== $index) {
419 1
                $errors    = $errorFactory->createErrorCollection();
420 1
                $formatter = $formatterFactory->createFormatter($messagesNamespace);
421 1
                $errors->addDataIdError($formatter->formatMessage($errorMessage));
422
423 4
                throw new JsonApiException($errors);
0 ignored issues
show
Documentation introduced by
$errors is of type object<Neomerx\JsonApi\Schema\ErrorCollection>, but the function expects a object<Neomerx\JsonApi\C...pi\Exceptions\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...
424
            }
425
        } else {
426
            // put the index to data for our convenience
427 1
            $jsonData[DI::KEYWORD_DATA][DI::KEYWORD_ID] = $index;
428
        }
429
        // validate the data
430 4
        $captures = $parser->assert($jsonData)->getCaptures();
431
432 3
        list ($index, $attributes, $toMany) = static::mapSchemaDataToModelData($captures, $schemaClass, $schemaInfo);
433
434
        try {
435 3
            $updated = $crud->update($index, $attributes, $toMany);
436 1
        } /** @noinspection PhpRedundantCatchClauseInspection */ catch (UniqueConstraintViolationException $exception) {
437 1
            $errors    = $errorFactory->createErrorCollection();
438 1
            $formatter = $formatterFactory->createFormatter($messagesNamespace);
439 1
            $title     = $formatter->formatMessage($errorMessage);
440 1
            $details   = null;
441 1
            $errorCode = JsonApiResponse::HTTP_CONFLICT;
442 1
            $errors->addDataError($title, $details, $errorCode);
443
444 1
            throw new JsonApiException($errors);
0 ignored issues
show
Documentation introduced by
$errors is of type object<Neomerx\JsonApi\Schema\ErrorCollection>, but the function expects a object<Neomerx\JsonApi\C...pi\Exceptions\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...
445
        }
446
447 2
        return $updated;
448
    }
449
450
    /**
451
     * @param int              $updated
452
     * @param string           $index
453
     * @param UriInterface     $requestUri
454
     * @param CrudInterface    $crud
455
     * @param EncoderInterface $encoder
456
     *
457
     * @return ResponseInterface
458
     *
459
     * @SuppressWarnings(PHPMD.ElseExpression)
460
     */
461 3
    protected static function defaultUpdateResponse(
462
        int $updated,
463
        string $index,
464
        UriInterface $requestUri,
465
        CrudInterface $crud,
466
        EncoderInterface $encoder
467
    ): ResponseInterface {
468 3
        $responses = static::defaultCreateResponses($requestUri, $encoder);
469 3
        if ($updated > 0 && ($model = $crud->read($index)) !== null) {
470 2
            assert(!($model instanceof PaginatedDataInterface));
471 2
            $response = $responses->getContentResponse($model);
472
        } else {
473 1
            $response = $responses->getCodeResponse(404);
474
        }
475
476 3
        return $response;
477
    }
478
479
    /**
480
     * @param string                      $index
481
     * @param UriInterface                $requestUri
482
     * @param JsonApiQueryParserInterface $queryParser
483
     * @param CrudInterface               $crud
484
     * @param EncoderInterface            $encoder
485
     *
486
     * @return ResponseInterface
487
     */
488 1
    protected static function defaultDeleteHandler(
489
        string $index,
490
        UriInterface $requestUri,
491
        JsonApiQueryParserInterface $queryParser,
492
        CrudInterface $crud,
493
        EncoderInterface $encoder
494
    ): ResponseInterface {
495 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
496 1
        $crud->remove($validatedIndex);
497
498 1
        $responses = static::defaultCreateResponses($requestUri, $encoder);
499 1
        $response  = $responses->getCodeResponse(204);
500
501 1
        return $response;
502
    }
503
504
    /** @noinspection PhpTooManyParametersInspection
505
     * @param string                      $index
506
     * @param string                      $jsonRelName
507
     * @param string                      $modelRelName
508
     * @param UriInterface                $requestUri
509
     * @param string                      $requestBody
510
     * @param string                      $schemaClass
511
     * @param ModelSchemaInfoInterface    $schemaInfo
512
     * @param JsonApiQueryParserInterface $queryParser
513
     * @param JsonApiDataParserInterface  $dataValidator
514
     * @param CrudInterface               $parentCrud
515
     * @param EncoderInterface            $encoder
516
     * @param FactoryInterface            $errorFactory
517
     * @param FormatterFactoryInterface   $formatterFactory
518
     *
519
     * @return ResponseInterface
520
     *
521
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
522
     */
523 1
    protected static function defaultAddInRelationshipHandler(
524
        string $index,
525
        string $jsonRelName,
526
        string $modelRelName,
527
        UriInterface $requestUri,
528
        string $requestBody,
529
        string $schemaClass,
530
        ModelSchemaInfoInterface $schemaInfo,
531
        JsonApiQueryParserInterface $queryParser,
532
        JsonApiDataParserInterface $dataValidator,
533
        CrudInterface $parentCrud,
534
        EncoderInterface $encoder,
535
        FactoryInterface $errorFactory,
536
        FormatterFactoryInterface $formatterFactory
537
    ): ResponseInterface {
538
        /** @var SchemaInterface $schemaClass */
539 1
        assert(array_key_exists(SchemaInterface::class, class_implements($schemaClass)) === true);
540 1
        $modelClass = $schemaClass::MODEL;
541 1
        assert($schemaInfo->hasRelationship($modelClass, $modelRelName));
542 1
        assert($schemaInfo->getRelationshipType($modelClass, $modelRelName) === RelationshipTypes::BELONGS_TO_MANY);
543
544 1
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
545 1
        $captures = $dataValidator->assertRelationship($index, $jsonRelName, $jsonData)->getCaptures();
546 1
        $relIds   = $captures[$jsonRelName];
547
548 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
549 1
        $parentCrud->createInBelongsToManyRelationship($validatedIndex, $modelRelName, $relIds);
550
551 1
        $responses = static::defaultCreateResponses($requestUri, $encoder);
552 1
        $response  = $responses->getCodeResponse(204);
553
554 1
        return $response;
555
    }
556
557
    /** @noinspection PhpTooManyParametersInspection
558
     * @param string                      $index
559
     * @param string                      $jsonRelName
560
     * @param string                      $modelRelName
561
     * @param UriInterface                $requestUri
562
     * @param string                      $requestBody
563
     * @param string                      $schemaClass
564
     * @param ModelSchemaInfoInterface    $schemaInfo
565
     * @param JsonApiQueryParserInterface $queryParser
566
     * @param JsonApiDataParserInterface  $dataValidator
567
     * @param CrudInterface               $parentCrud
568
     * @param EncoderInterface            $encoder
569
     * @param FactoryInterface            $errorFactory
570
     * @param FormatterFactoryInterface   $formatterFactory
571
     *
572
     * @return ResponseInterface
573
     *
574
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
575
     */
576 1
    protected static function defaultDeleteInRelationshipHandler(
577
        string $index,
578
        string $jsonRelName,
579
        string $modelRelName,
580
        UriInterface $requestUri,
581
        string $requestBody,
582
        string $schemaClass,
583
        ModelSchemaInfoInterface $schemaInfo,
584
        JsonApiQueryParserInterface $queryParser,
585
        JsonApiDataParserInterface $dataValidator,
586
        CrudInterface $parentCrud,
587
        EncoderInterface $encoder,
588
        FactoryInterface $errorFactory,
589
        FormatterFactoryInterface $formatterFactory
590
    ): ResponseInterface {
591
        /** @var SchemaInterface $schemaClass */
592 1
        assert(array_key_exists(SchemaInterface::class, class_implements($schemaClass)) === true);
593 1
        $modelClass = $schemaClass::MODEL;
594 1
        assert($schemaInfo->hasRelationship($modelClass, $modelRelName));
595 1
        assert($schemaInfo->getRelationshipType($modelClass, $modelRelName) === RelationshipTypes::BELONGS_TO_MANY);
596
597 1
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
598 1
        $captures = $dataValidator->assertRelationship($index, $jsonRelName, $jsonData)->getCaptures();
599 1
        $relIds   = $captures[$jsonRelName];
600
601 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
602 1
        $parentCrud->removeInBelongsToManyRelationship($validatedIndex, $modelRelName, $relIds);
603
604 1
        $responses = static::defaultCreateResponses($requestUri, $encoder);
605 1
        $response  = $responses->getCodeResponse(204);
606
607 1
        return $response;
608
    }
609
610
    /** @noinspection PhpTooManyParametersInspection
611
     * @param string                      $index
612
     * @param string                      $jsonRelName
613
     * @param string                      $modelRelName
614
     * @param UriInterface                $requestUri
615
     * @param string                      $requestBody
616
     * @param string                      $schemaClass
617
     * @param ModelSchemaInfoInterface    $schemaInfo
618
     * @param JsonApiQueryParserInterface $queryParser
619
     * @param JsonApiDataParserInterface  $dataValidator
620
     * @param CrudInterface               $crud
621
     * @param EncoderInterface            $encoder
622
     * @param FactoryInterface            $errorFactory
623
     * @param FormatterFactoryInterface   $formatterFactory
624
     *
625
     * @return ResponseInterface
626
     *
627
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
628
     */
629 1
    protected static function defaultReplaceInRelationship(
630
        string $index,
631
        string $jsonRelName,
632
        string $modelRelName,
633
        UriInterface $requestUri,
634
        string $requestBody,
635
        string $schemaClass,
636
        ModelSchemaInfoInterface $schemaInfo,
637
        JsonApiQueryParserInterface $queryParser,
638
        JsonApiDataParserInterface $dataValidator,
639
        CrudInterface $crud,
640
        EncoderInterface $encoder,
641
        FactoryInterface $errorFactory,
642
        FormatterFactoryInterface $formatterFactory
643
    ): ResponseInterface {
644
        /** @var SchemaInterface $schemaClass */
645 1
        assert(array_key_exists(SchemaInterface::class, class_implements($schemaClass)) === true);
646 1
        $modelClass = $schemaClass::MODEL;
647 1
        assert($schemaInfo->hasRelationship($modelClass, $modelRelName));
648 1
        assert(
649 1
            ($type =$schemaInfo->getRelationshipType($modelClass, $modelRelName)) === RelationshipTypes::BELONGS_TO ||
650 1
            $type === RelationshipTypes::BELONGS_TO_MANY
651
        );
652
653 1
        $jsonData = static::readJsonFromRequest($requestBody, $errorFactory, $formatterFactory);
654 1
        $captures = $dataValidator->assertRelationship($index, $jsonRelName, $jsonData)->getCaptures();
655
656
        // If we are here then we have something in 'data' section.
657
658 1
        $validatedIndex = $queryParser->parse($index)->getIdentity();
659 1
        list (, $attributes, $toMany) = static::mapSchemaDataToModelData($captures, $schemaClass, $schemaInfo);
0 ignored issues
show
Documentation introduced by
$schemaClass is of type object<Limoncello\Flute\...Schema\SchemaInterface>, but the function expects a string.

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...
660
661 1
        $updated = $crud->update($validatedIndex, $attributes, $toMany);
662
663 1
        return static::defaultUpdateResponse($updated, $index, $requestUri, $crud, $encoder);
664
    }
665
666
    /**
667
     * @param ContainerInterface $container
668
     * @param string             $rulesClass
669
     *
670
     * @return JsonApiQueryParserInterface
671
     *
672
     * @throws ContainerExceptionInterface
673
     * @throws NotFoundExceptionInterface
674
     */
675 20
    protected static function defaultCreateQueryParser(
676
        ContainerInterface $container,
677
        string $rulesClass = DefaultQueryValidationRules::class
678
    ): JsonApiQueryParserInterface {
679 20
        static::assertClassImplements($rulesClass, JsonApiQueryRulesInterface::class);
680
681
        /** @var JsonApiParserFactoryInterface $factory */
682 20
        $factory = $container->get(JsonApiParserFactoryInterface::class);
683 20
        $parser  = $factory->createQueryParser($rulesClass);
684
685 20
        return $parser;
686
    }
687
688
    /**
689
     * @param ContainerInterface $container
690
     * @param string             $rulesClass
691
     *
692
     * @return JsonApiDataParserInterface
693
     *
694
     * @throws ContainerExceptionInterface
695
     * @throws NotFoundExceptionInterface
696
     */
697 11
    protected static function defaultCreateDataParser(
698
        ContainerInterface $container,
699
        string $rulesClass
700
    ): JsonApiDataParserInterface {
701 11
        static::assertClassImplements($rulesClass, JsonApiDataRulesInterface::class);
702
703
        /** @var JsonApiParserFactoryInterface $factory */
704 11
        $factory = $container->get(JsonApiParserFactoryInterface::class);
705 11
        $parser  = $factory->createDataParser($rulesClass);
706
707 11
        return $parser;
708
    }
709
710
    /**
711
     * @param ContainerInterface $container
712
     * @param string             $rulesClass
713
     *
714
     * @return FormValidatorInterface
715
     *
716
     * @throws ContainerExceptionInterface
717
     * @throws NotFoundExceptionInterface
718
     */
719 1
    protected static function defaultCreateFormValidator(
720
        ContainerInterface $container,
721
        string $rulesClass
722
    ): FormValidatorInterface {
723 1
        static::assertClassImplements($rulesClass, FormRulesInterface::class);
724
725
        /** @var FormValidatorFactoryInterface $factory */
726 1
        $factory   = $container->get(FormValidatorFactoryInterface::class);
727 1
        $validator = $factory->createValidator($rulesClass);
728
729 1
        return $validator;
730
    }
731
732
    /**
733
     * @param ContainerInterface $container
734
     * @param string             $schemaClass
735
     *
736
     * @return ParametersMapperInterface
737
     *
738
     * @throws ContainerExceptionInterface
739
     * @throws NotFoundExceptionInterface
740
     */
741 16
    protected static function defaultCreateParameterMapper(
742
        ContainerInterface $container,
743
        string $schemaClass
744
    ): ParametersMapperInterface {
745 16
        static::assertClassImplements($schemaClass, SchemaInterface::class);
746
747
        /** @var SchemaInterface $schemaClass */
748 16
        $jsonResourceType = $schemaClass::TYPE;
749
750
        /** @var ParametersMapperInterface $mapper */
751 16
        $mapper = $container->get(ParametersMapperInterface::class);
752 16
        $mapper->selectRootSchemaByResourceType($jsonResourceType);
753
754 16
        return $mapper;
755
    }
756
757
    /**
758
     * @param JsonApiQueryParserInterface $queryParser
759
     * @param EncoderInterface            $encoder
760
     *
761
     * @return void
762
     */
763 14
    protected static function defaultApplyIncludesAndFieldSetsToEncoder(
764
        JsonApiQueryParserInterface $queryParser,
765
        EncoderInterface $encoder
766
    ): void {
767 14
        if ($queryParser->hasIncludes() === true) {
768 3
            $paths = array_keys($queryParser->getIncludes());
769 3
            $encoder->withIncludedPaths($paths);
0 ignored issues
show
Documentation introduced by
$paths is of type array, but the function expects a object<Neomerx\JsonApi\C...racts\Encoder\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...
770
        }
771 14
        if ($queryParser->hasFields() === true) {
772 1
            $encoder->withFieldSets($queryParser->getFields());
773
        }
774
    }
775
776
    /**
777
     * @param ContainerInterface $container
778
     * @param string|null        $class
779
     *
780
     * @return CrudInterface
781
     *
782
     * @throws ContainerExceptionInterface
783
     * @throws NotFoundExceptionInterface
784
     */
785 28
    protected static function defaultCreateApi(ContainerInterface $container, string $class): CrudInterface
786
    {
787 28
        static::assertClassImplements($class, CrudInterface::class);
788
789
        /** @var FactoryInterface $factory */
790 28
        $factory = $container->get(FactoryInterface::class);
791 28
        $api     = $factory->createApi($class);
792
793 28
        return $api;
794
    }
795
796
    /**
797
     * @param UriInterface     $requestUri
798
     * @param EncoderInterface $encoder
799
     *
800
     * @return ResponsesInterface
801
     */
802 21
    protected static function defaultCreateResponses(
803
        UriInterface $requestUri,
804
        EncoderInterface $encoder
805
    ): ResponsesInterface {
806 21
        $encoder->forOriginalUri($requestUri);
807 21
        $responses = new Responses(
808 21
            new MediaType(MediaTypeInterface::JSON_API_TYPE, MediaTypeInterface::JSON_API_SUB_TYPE),
809 21
            $encoder
810
        );
811
812 21
        return $responses;
813
    }
814
815
    /**
816
     * Developers can override the method in order to add/remove some data for `create`/`update` inputs.
817
     *
818
     * @param string                    $requestBody
819
     * @param FactoryInterface          $errorFactory
820
     * @param FormatterFactoryInterface $formatterFactory
821
     * @param string                    $messagesNamespace
822
     * @param string                    $errorMessage
823
     *
824
     * @return array
825
     */
826 11
    protected static function readJsonFromRequest(
827
        string $requestBody,
828
        FactoryInterface $errorFactory,
829
        FormatterFactoryInterface $formatterFactory,
830
        string $messagesNamespace = S::GENERIC_NAMESPACE,
831
        string $errorMessage = Generic::MSG_ERR_INVALID_ELEMENT
832
    ): array {
833 11
        if (empty($requestBody) === true || ($json = json_decode($requestBody, true)) === null) {
834 1
            $formatter = $formatterFactory->createFormatter($messagesNamespace);
835 1
            $errors    = $errorFactory->createErrorCollection();
836 1
            $errors->addDataError($formatter->formatMessage($errorMessage));
837
838 1
            throw new JsonApiException($errors);
0 ignored issues
show
Documentation introduced by
$errors is of type object<Neomerx\JsonApi\Schema\ErrorCollection>, but the function expects a object<Neomerx\JsonApi\C...pi\Exceptions\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...
839
        }
840
841 10
        return $json;
842
    }
843
844
    /**
845
     * Developers can override the method in order to use custom data mapping from a Schema to Model.
846
     *
847
     * @param iterable                 $captures
848
     * @param string                   $schemaClass
849
     * @param ModelSchemaInfoInterface $schemaInfo
850
     *
851
     * @return array
852
     */
853 6
    protected static function mapSchemaDataToModelData(
854
        iterable $captures,
855
        string $schemaClass,
856
        ModelSchemaInfoInterface $schemaInfo
857
    ): array {
858 6
        static::assertClassImplements($schemaClass, SchemaInterface::class);
859
860
        /** @var SchemaInterface $schemaClass */
861 6
        static::assertClassImplements($modelClass = $schemaClass::MODEL, ModelInterface::class);
862
        /** @var ModelInterface $modelClass */
863
864 6
        $index         = null;
865 6
        $fields        = [];
866 6
        $toManyIndexes = [];
867 6
        foreach ($captures as $name => $value) {
868 6
            assert(is_string($name) === true);
869 6
            if ($name === DI::KEYWORD_ID) {
870 4
                $index = $value;
871 6
            } elseif ($schemaClass::hasAttributeMapping($name) === true) {
872 5
                $fieldName          = $schemaClass::getAttributeMapping($name);
873 5
                $fields[$fieldName] = $value;
874 6
            } elseif ($schemaClass::hasRelationshipMapping($name) === true) {
875 3
                $modelRelName = $schemaClass::getRelationshipMapping($name);
876 3
                $relType      = $schemaInfo->getRelationshipType($modelClass, $modelRelName);
0 ignored issues
show
Documentation introduced by
$modelClass is of type object<Limoncello\Contra...ication\ModelInterface>, but the function expects a string.

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...
877 3
                if ($relType === RelationshipTypes::BELONGS_TO) {
878 3
                    $fkName          = $schemaInfo->getForeignKey($modelClass, $modelRelName);
0 ignored issues
show
Documentation introduced by
$modelClass is of type object<Limoncello\Contra...ication\ModelInterface>, but the function expects a string.

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...
879 3
                    $fields[$fkName] = $value;
880 2
                } elseif ($relType === RelationshipTypes::BELONGS_TO_MANY) {
881 6
                    $toManyIndexes[$modelRelName] = $value;
882
                }
883
            }
884
        }
885
886 6
        $result = [$index, $fields, $toManyIndexes];
887
888 6
        return $result;
889
    }
890
891
    /**
892
     * @param mixed                $model
893
     * @param UriInterface         $requestUri
894
     * @param JsonSchemasInterface $jsonSchemas
895
     *
896
     * @return string
897
     */
898 1
    protected static function defaultGetResourceUrl(
899
        $model,
900
        UriInterface $requestUri,
901
        JsonSchemasInterface $jsonSchemas
902
    ): string {
903 1
        $schema    = $jsonSchemas->getSchema($model);
904 1
        $selfLink  = $schema->getSelfLink($model);
905 1
        $urlPrefix = (string)$requestUri->withPath('')->withQuery('')->withFragment('');
906 1
        $fullUrl   = $selfLink->getStringRepresentation($urlPrefix);
907
908 1
        return $fullUrl;
909
    }
910
911
    /**
912
     * @param null|string $value
913
     *
914
     * @return void
915
     */
916 28
    private static function assertClassValueDefined(?string $value): void
917
    {
918 28
        assert(empty($value) === false, 'Value should be defined in `' . static::class . '`.');
919
    }
920
921
    /**
922
     * @param string $class
923
     * @param string $interface
924
     *
925
     * @return void
926
     */
927 29
    private static function assertClassImplements(string $class, string $interface): void
928
    {
929 29
        assert(
930 29
            array_key_exists($interface, class_implements($class)) === true,
931 29
            "Class `$class` should implement `" . $interface . '` interface.'
932
        );
933
    }
934
}
935