Completed
Push — master ( 0ca994...bcdd07 )
by Neomerx
05:23
created

Validator::createMultiData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 3

1 Method

Rating   Name   Duplication   Size   Complexity  
A Validator::reInitAggregatorsIfNeeded() 0 4 2
1
<?php namespace Limoncello\Flute\Validation;
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\L10n\FormatterFactoryInterface;
20
use Limoncello\Contracts\L10n\FormatterInterface;
21
use Limoncello\Flute\Contracts\Validation\ErrorCodes;
22
use Limoncello\Flute\Contracts\Validation\JsonApiValidatorInterface;
23
use Limoncello\Flute\Http\JsonApiResponse;
24
use Limoncello\Flute\Validation\Execution\ContextStorage;
25
use Limoncello\Flute\Validation\Execution\JsonApiErrorCollection;
26
use Limoncello\Flute\Validation\Execution\JsonApiRuleSerializer;
27
use Limoncello\Flute\Validation\Rules\RelationshipsTrait;
28
use Limoncello\Validation\Contracts\Errors\ErrorInterface;
29
use Limoncello\Validation\Contracts\Execution\ContextStorageInterface;
30
use Limoncello\Validation\Execution\BlockInterpreter;
31
use Limoncello\Validation\Validator\BaseValidator;
32
use Neomerx\JsonApi\Contracts\Document\DocumentInterface as DI;
33
use Neomerx\JsonApi\Exceptions\JsonApiException;
34
use Psr\Container\ContainerInterface;
35
36
/**
37
 * @package Limoncello\Flute
38
 *
39
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
40
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
41
 */
42
class Validator extends BaseValidator implements JsonApiValidatorInterface
43
{
44
    use RelationshipsTrait;
45
46
    /**
47
     * Namespace for string resources.
48
     */
49
    const RESOURCES_NAMESPACE = 'Limoncello.Flute.Validation';
50
51
    /** Rule description index */
52
    const RULE_INDEX = 0;
53
54
    /** Rule description index */
55
    const RULE_ATTRIBUTES = self::RULE_INDEX + 1;
56
57
    /** Rule description index */
58
    const RULE_TO_ONE = self::RULE_ATTRIBUTES + 1;
59
60
    /** Rule description index */
61
    const RULE_TO_MANY = self::RULE_TO_ONE + 1;
62
63
    /** Rule description index */
64
    const RULE_UNLISTED_ATTRIBUTE = self::RULE_TO_MANY + 1;
65
66
    /** Rule description index */
67
    const RULE_UNLISTED_RELATIONSHIP = self::RULE_UNLISTED_ATTRIBUTE + 1;
68
69
    /**
70
     * @var ContainerInterface
71
     */
72
    private $container;
73
74
    /**
75
     * @var int
76
     */
77
    private $errorStatus;
78
79
    /**
80
     * @var ContextStorageInterface
81
     */
82
    private $contextStorage;
83
84
    /**
85
     * @var JsonApiErrorCollection
86
     */
87
    private $jsonApiErrors;
88
89
    /**
90
     * @var array
91
     */
92
    private $blocks;
93
94
    /**
95
     * @var array
96
     */
97
    private $idRule;
98
99
    /**
100
     * @var array
101
     */
102
    private $typeRule;
103
104
    /**
105
     * @var int[]
106
     */
107
    private $attributeRules;
108
109
    /**
110
     * @var int[]
111
     */
112
    private $toOneRules;
113
114
    /**
115
     * @var int[]
116
     */
117
    private $toManyRules;
118
119
    /**
120
     * @var bool
121
     */
122
    private $isIgnoreUnknowns;
123
124
    /**
125
     * @var FormatterInterface|null
126
     */
127
    private $messageFormatter;
128
129
    /**
130
     * @param string             $name
131
     * @param array              $data
132
     * @param ContainerInterface $container
133
     * @param int                $errorStatus
134
     *
135
     * @SuppressWarnings(PHPMD.StaticAccess)
136
     */
137
    public function __construct(
138
        string $name,
139
        array $data,
140
        ContainerInterface $container,
141
        int $errorStatus = JsonApiResponse::HTTP_UNPROCESSABLE_ENTITY
142
    ) {
143
        $ruleSet           = JsonApiRuleSerializer::extractRuleSet($name, $data);
144
        $this->blocks      = JsonApiRuleSerializer::extractBlocks($data);
145
        $this->container   = $container;
146
        $this->idRule      = JsonApiRuleSerializer::getIdRule($ruleSet);
147
        $this->typeRule    = JsonApiRuleSerializer::getTypeRule($ruleSet);
148
        $this->errorStatus = $errorStatus;
149
150
        $this
151
            ->setAttributeRules(JsonApiRuleSerializer::getAttributeRules($ruleSet))
152
            ->setToOneIndexes(JsonApiRuleSerializer::getToOneRules($ruleSet))
153
            ->setToManyIndexes(JsonApiRuleSerializer::getToManyRules($ruleSet))
154
            ->disableIgnoreUnknowns();
155
156
        parent::__construct();
157
    }
158
159
    /**
160
     * @inheritdoc
161
     */
162
    public function assert($jsonData): JsonApiValidatorInterface
163
    {
164
        if ($this->validate($jsonData) === false) {
165
            throw new JsonApiException($this->getJsonApiErrorCollection(), $this->getErrorStatus());
166
        }
167
168
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Limoncello\Flute\Validation\Validator) is incompatible with the return type declared by the interface Limoncello\Flute\Contrac...idatorInterface::assert of type self.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
169
    }
170
171
    /**
172
     * @inheritdoc
173
     */
174
    public function validate($input): bool
175
    {
176
        $this->reInitAggregatorsIfNeeded();
177
178
        $this
179
            ->validateType($input)
180
            ->validateId($input)
181
            ->validateAttributes($input)
182
            ->validateRelationships($input);
183
184
        $hasNoErrors = $this->getJsonApiErrorCollection()->count() <= 0;
185
186
        return $hasNoErrors;
187
    }
188
189
    /**
190
     * @inheritdoc
191
     */
192
    public function getJsonApiErrors(): array
193
    {
194
        return $this->getJsonApiErrorCollection()->getArrayCopy();
195
    }
196
197
    /**
198
     * @inheritdoc
199
     */
200
    public function getJsonApiCaptures(): array
201
    {
202
        return $this->getCaptures()->get();
203
    }
204
205
    /**
206
     * @return BaseValidator
207
     */
208
    protected function resetAggregators(): BaseValidator
209
    {
210
        $self = parent::resetAggregators();
211
212
        $this->jsonApiErrors  = $this->createJsonApiErrors();
213
        $this->contextStorage = $this->createContextStorage();
214
215
        return $self;
216
    }
217
218
    /**
219
     * @param array $jsonData
220
     *
221
     * @return self
222
     *
223
     * @SuppressWarnings(PHPMD.StaticAccess)
224
     * @SuppressWarnings(PHPMD.ElseExpression)
225
     */
226
    private function validateType(array $jsonData): self
227
    {
228
        // execute start(s)
229
        $starts = JsonApiRuleSerializer::getRuleStartIndexes($this->getTypeRule());
230
        $this->executeStarts($starts);
231
232
        if (array_key_exists(DI::KEYWORD_DATA, $jsonData) === true &&
233
            array_key_exists(DI::KEYWORD_TYPE, $data = $jsonData[DI::KEYWORD_DATA]) === true
234
        ) {
235
            // execute main validation block(s)
236
            $index = JsonApiRuleSerializer::getRuleIndex($this->getTypeRule());
237
            $this->executeBlock($data[DI::KEYWORD_TYPE], $index);
238
        } else {
239
            $title   = $this->formatMessage(ErrorCodes::INVALID_VALUE);
240
            $details = $this->formatMessage(ErrorCodes::TYPE_MISSING);
241
            $this->getJsonApiErrorCollection()->addDataTypeError($title, $details, $this->getErrorStatus());
242
        }
243
244
        // execute end(s)
245
        $ends = JsonApiRuleSerializer::getRuleEndIndexes($this->getTypeRule());
246
        $this->executeEnds($ends);
247
248 View Code Duplication
        if (count($this->getErrors()) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
249
            $title = $this->formatMessage(ErrorCodes::INVALID_VALUE);
250
            foreach ($this->getErrors()->get() as $error) {
251
                $this->getJsonApiErrorCollection()
252
                    ->addDataTypeError($title, $this->getMessage($error), $this->getErrorStatus());
253
            }
254
            $this->getErrors()->clear();
255
        }
256
257
        return $this;
258
    }
259
260
    /**
261
     * @param array $jsonData
262
     *
263
     * @return self
264
     *
265
     * @SuppressWarnings(PHPMD.StaticAccess)
266
     */
267
    private function validateId(array $jsonData): self
268
    {
269
        // execute start(s)
270
        $starts = JsonApiRuleSerializer::getRuleStartIndexes($this->getIdRule());
271
        $this->executeStarts($starts);
272
273
        // execute main validation block(s)
274
        if (array_key_exists(DI::KEYWORD_DATA, $jsonData) === true &&
275
            array_key_exists(DI::KEYWORD_ID, $data = $jsonData[DI::KEYWORD_DATA]) === true
276
        ) {
277
            $index = JsonApiRuleSerializer::getRuleIndex($this->getIdRule());
278
            $this->executeBlock($data[DI::KEYWORD_ID], $index);
279
        }
280
281
        // execute end(s)
282
        $ends = JsonApiRuleSerializer::getRuleEndIndexes($this->getIdRule());
283
        $this->executeEnds($ends);
284
285 View Code Duplication
        if (count($this->getErrors()) > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
286
            $title = $this->formatMessage(ErrorCodes::INVALID_VALUE);
287
            foreach ($this->getErrors()->get() as $error) {
288
                $this->getJsonApiErrorCollection()
289
                    ->addDataIdError($title, $this->getMessage($error), $this->getErrorStatus());
290
            }
291
            $this->getErrors()->clear();
292
        }
293
294
        return $this;
295
    }
296
297
    /**
298
     * @param array $jsonData
299
     *
300
     * @return self
301
     *
302
     * @SuppressWarnings(PHPMD.StaticAccess)
303
     * @SuppressWarnings(PHPMD.ElseExpression)
304
     */
305
    private function validateAttributes(array $jsonData): self
306
    {
307
        // execute start(s)
308
        $starts = JsonApiRuleSerializer::getRulesStartIndexes($this->getAttributeRules());
309
        $this->executeStarts($starts);
310
311
        if (array_key_exists(DI::KEYWORD_DATA, $jsonData) === true &&
312
            array_key_exists(DI::KEYWORD_ATTRIBUTES, $data = $jsonData[DI::KEYWORD_DATA]) === true
313
        ) {
314
            if (is_array($attributes = $data[DI::KEYWORD_ATTRIBUTES]) === false) {
315
                $title   = $this->formatMessage(ErrorCodes::INVALID_VALUE);
316
                $details = $this->formatMessage(ErrorCodes::INVALID_ATTRIBUTES);
317
                $this->getJsonApiErrorCollection()->addAttributesError($title, $details, $this->getErrorStatus());
318
            } else {
319
                // execute main validation block(s)
320
                foreach ($attributes as $name => $value) {
321
                    if ($this->hasAttributeIndex($name) === true) {
322
                        $this->executeBlock($value, $this->getAttributeIndex($name));
323 View Code Duplication
                    } elseif ($this->isIgnoreUnknowns() === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
324
                        $title   = $this->formatMessage(ErrorCodes::INVALID_VALUE);
325
                        $details = $this->formatMessage(ErrorCodes::UNKNOWN_ATTRIBUTE);
326
                        $status  = $this->getErrorStatus();
327
                        $this->getJsonApiErrorCollection()->addDataAttributeError($name, $title, $details, $status);
328
                    }
329
                }
330
            }
331
        }
332
333
        // execute end(s)
334
        $ends = JsonApiRuleSerializer::getRulesEndIndexes($this->getAttributeRules());
335
        $this->executeEnds($ends);
336
337
        if (count($this->getErrors()) > 0) {
338
            foreach ($this->getErrors()->get() as $error) {
339
                $this->getJsonApiErrorCollection()->addValidationAttributeError($error);
340
            }
341
            $this->getErrors()->clear();
342
        }
343
344
        return $this;
345
    }
346
347
    /**
348
     * @param array $jsonData
349
     *
350
     * @return self
351
     *
352
     * @SuppressWarnings(PHPMD.StaticAccess)
353
     * @SuppressWarnings(PHPMD.ElseExpression)
354
     */
355
    private function validateRelationships(array $jsonData): self
356
    {
357
        // execute start(s)
358
        $starts = array_merge(
359
            JsonApiRuleSerializer::getRulesStartIndexes($this->getToOneRules()),
360
            JsonApiRuleSerializer::getRulesStartIndexes($this->getToManyRules())
361
        );
362
        $this->executeStarts($starts);
363
364
        if (array_key_exists(DI::KEYWORD_DATA, $jsonData) === true &&
365
            array_key_exists(DI::KEYWORD_RELATIONSHIPS, $data = $jsonData[DI::KEYWORD_DATA]) === true
366
        ) {
367
            if (is_array($relationships = $data[DI::KEYWORD_RELATIONSHIPS]) === false) {
368
                $title   = $this->formatMessage(ErrorCodes::INVALID_VALUE);
369
                $details = $this->formatMessage(ErrorCodes::INVALID_RELATIONSHIP_TYPE);
370
                $this->getJsonApiErrorCollection()->addRelationshipsError($title, $details, $this->getErrorStatus());
371
            } else {
372
                // ok we got to something that could be null or a valid relationship
373
                $toOneIndexes  = JsonApiRuleSerializer::getRulesIndexes($this->getToOneRules());
374
                $toManyIndexes = JsonApiRuleSerializer::getRulesIndexes($this->getToManyRules());
375
376
                foreach ($relationships as $name => $relationship) {
377
                    if (array_key_exists($name, $toOneIndexes) === true) {
378
                        // it might be to1 relationship
379
                        $this->validateAsToOneRelationship($toOneIndexes[$name], $name, $relationship);
380
                    } elseif (array_key_exists($name, $toManyIndexes) === true) {
381
                        // it might be toMany relationship
382
                        $this->validateAsToManyRelationship($toManyIndexes[$name], $name, $relationship);
383 View Code Duplication
                    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
384
                        // unknown relationship
385
                        $title   = $this->formatMessage(ErrorCodes::INVALID_VALUE);
386
                        $details = $this->formatMessage(ErrorCodes::UNKNOWN_RELATIONSHIP);
387
                        $status  = $this->getErrorStatus();
388
                        $this->getJsonApiErrorCollection()->addRelationshipError($name, $title, $details, $status);
389
                    }
390
                }
391
            }
392
        }
393
394
        // execute end(s)
395
        $ends = array_merge(
396
            JsonApiRuleSerializer::getRulesEndIndexes($this->getToOneRules()),
397
            JsonApiRuleSerializer::getRulesEndIndexes($this->getToManyRules())
398
        );
399
        $this->executeEnds($ends);
400
401
        if (count($this->getErrors()) > 0) {
402
            foreach ($this->getErrors()->get() as $error) {
403
                $this->getJsonApiErrorCollection()->addValidationRelationshipError($error);
404
            }
405
            $this->getErrors()->clear();
406
        }
407
408
        return $this;
409
    }
410
411
    /**
412
     * @param int    $index
413
     * @param string $name
414
     * @param mixed  $mightBeRelationship
415
     *
416
     * @return void
417
     *
418
     * @SuppressWarnings(PHPMD.ElseExpression)
419
     */
420
    private function validateAsToOneRelationship(int $index, string $name, $mightBeRelationship): void
421
    {
422
        if (is_array($mightBeRelationship) === true &&
423
            array_key_exists(DI::KEYWORD_DATA, $mightBeRelationship) === true &&
424
            ($parsed = $this->parseSingleRelationship($mightBeRelationship[DI::KEYWORD_DATA])) !== false
425
        ) {
426
            // All right we got something. Now pass it to a validation rule.
427
            $this->executeBlock($parsed, $index);
428
        } else {
429
            $title   = $this->formatMessage(ErrorCodes::INVALID_VALUE);
430
            $details = $this->formatMessage(ErrorCodes::INVALID_RELATIONSHIP);
431
            $this->getJsonApiErrorCollection()->addRelationshipError($name, $title, $details, $this->getErrorStatus());
432
        }
433
    }
434
435
    /**
436
     * @param int    $index
437
     * @param string $name
438
     * @param mixed  $mightBeRelationship
439
     *
440
     * @return void
441
     *
442
     * @SuppressWarnings(PHPMD.ElseExpression)
443
     */
444
    private function validateAsToManyRelationship(int $index, string $name, $mightBeRelationship): void
445
    {
446
        $isParsed       = true;
447
        $collectedPairs = [];
448
        if (is_array($mightBeRelationship) === true &&
449
            array_key_exists(DI::KEYWORD_DATA, $mightBeRelationship) === true &&
450
            is_array($data = $mightBeRelationship[DI::KEYWORD_DATA]) === true
451
        ) {
452
            foreach ($data as $mightTypeAndId) {
453
                // we accept only pairs of type and id (no `null`s are accepted).
454
                if (is_array($parsed = $this->parseSingleRelationship($mightTypeAndId)) === true) {
455
                    $collectedPairs[] = $parsed;
456
                } else {
457
                    $isParsed = false;
458
                    break;
459
                }
460
            }
461
        } else {
462
            $isParsed = false;
463
        }
464
465 View Code Duplication
        if ($isParsed === true) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
466
            // All right we got something. Now pass it to a validation rule.
467
            $this->executeBlock($collectedPairs, $index);
468
        } else {
469
            $title   = $this->formatMessage(ErrorCodes::INVALID_VALUE);
470
            $details = $this->formatMessage(ErrorCodes::INVALID_RELATIONSHIP);
471
            $this->getJsonApiErrorCollection()->addRelationshipError($name, $title, $details, $this->getErrorStatus());
472
        }
473
    }
474
475
    /**
476
     * @param mixed $data
477
     *
478
     * @return array|null|false Either `array` ($type => $id), or `null`, or `false` on error.
479
     *
480
     * @SuppressWarnings(PHPMD.ElseExpression)
481
     */
482
    private function parseSingleRelationship($data)
483
    {
484
        if ($data === null) {
485
            $result = null;
486
        } elseif (is_array($data) === true &&
487
            array_key_exists(DI::KEYWORD_TYPE, $data) === true &&
488
            array_key_exists(DI::KEYWORD_ID, $data) === true &&
489
            is_scalar($type = $data[DI::KEYWORD_TYPE]) === true &&
490
            is_scalar($index = $data[DI::KEYWORD_ID]) === true
491
        ) {
492
            $result = [$type => $index];
493
        } else {
494
            $result = false;
495
        }
496
497
        return $result;
498
    }
499
500
    /**
501
     * Re-initializes internal aggregators for captures, errors, etc.
502
     */
503
    private function reInitAggregatorsIfNeeded(): void
504
    {
505
        $this->areAggregatorsDirty() === false ?: $this->resetAggregators();
506
    }
507
508
    /**
509
     * @param mixed $input
510
     * @param int   $index
511
     *
512
     * @return void
513
     *
514
     * @SuppressWarnings(PHPMD.StaticAccess)
515
     */
516
    private function executeBlock($input, int $index): void
517
    {
518
        BlockInterpreter::executeBlock(
519
            $input,
520
            $index,
521
            $this->getBlocks(),
522
            $this->getContextStorage(),
523
            $this->getCaptures(),
524
            $this->getErrors()
525
        );
526
    }
527
528
    /**
529
     * @param array $indexes
530
     *
531
     * @return void
532
     *
533
     * @SuppressWarnings(PHPMD.StaticAccess)
534
     */
535
    private function executeStarts(array $indexes): void
536
    {
537
        BlockInterpreter::executeStarts($indexes, $this->getBlocks(), $this->getContextStorage(), $this->getErrors());
538
    }
539
540
    /**
541
     * @param array $indexes
542
     *
543
     * @return void
544
     *
545
     * @SuppressWarnings(PHPMD.StaticAccess)
546
     */
547
    private function executeEnds(array $indexes): void
548
    {
549
        BlockInterpreter::executeEnds($indexes, $this->getBlocks(), $this->getContextStorage(), $this->getErrors());
550
    }
551
552
    /**
553
     * @param ErrorInterface $error
554
     *
555
     * @return string
556
     */
557 View Code Duplication
    private function getMessage(ErrorInterface $error): string
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...
558
    {
559
        $context = $error->getMessageContext();
560
        $args    = $context === null ? [] : $context;
561
        $message = $this->formatMessage($error->getMessageCode(), $args);
562
563
        return $message;
564
    }
565
566
    /**
567
     * @return array
568
     */
569
    protected function getIdRule(): array
570
    {
571
        return $this->idRule;
572
    }
573
574
    /**
575
     * @return array
576
     */
577
    protected function getTypeRule(): array
578
    {
579
        return $this->typeRule;
580
    }
581
582
    /**
583
     * @return ContextStorageInterface
584
     */
585
    protected function getContextStorage(): ContextStorageInterface
586
    {
587
        return $this->contextStorage;
588
    }
589
590
    /**
591
     * @return ContextStorageInterface
592
     */
593
    protected function createContextStorage(): ContextStorageInterface
594
    {
595
        return new ContextStorage($this->getContainer(), $this->getBlocks());
596
    }
597
598
    /**
599
     * @return JsonApiErrorCollection
600
     */
601
    protected function getJsonApiErrorCollection(): JsonApiErrorCollection
602
    {
603
        return $this->jsonApiErrors;
604
    }
605
606
    /**
607
     * @return JsonApiErrorCollection
608
     */
609
    protected function createJsonApiErrors(): JsonApiErrorCollection
610
    {
611
        return new JsonApiErrorCollection($this->getContainer(), $this->getErrorStatus());
612
    }
613
614
    /**
615
     * @return ContainerInterface
616
     */
617
    protected function getContainer(): ContainerInterface
618
    {
619
        return $this->container;
620
    }
621
622
    /**
623
     * @return int
624
     */
625
    protected function getErrorStatus(): int
626
    {
627
        return $this->errorStatus;
628
    }
629
630
    /**
631
     * @return bool
632
     */
633
    protected function isIgnoreUnknowns(): bool
634
    {
635
        return $this->isIgnoreUnknowns;
636
    }
637
638
    /**
639
     * @return Validator
640
     */
641
    protected function enableIgnoreUnknowns(): self
642
    {
643
        $this->isIgnoreUnknowns = true;
644
645
        return $this;
646
    }
647
648
    /**
649
     * @return Validator
650
     */
651
    protected function disableIgnoreUnknowns(): self
652
    {
653
        $this->isIgnoreUnknowns = false;
654
655
        return $this;
656
    }
657
658
    /**
659
     * @param array $rules
660
     *
661
     * @return self
662
     */
663
    private function setAttributeRules(array $rules): self
664
    {
665
        assert($this->debugCheckIndexesExist($rules));
666
667
        $this->attributeRules = $rules;
668
669
        return $this;
670
    }
671
672
    /**
673
     * @param array $rules
674
     *
675
     * @return self
676
     */
677
    private function setToOneIndexes(array $rules): self
678
    {
679
        assert($this->debugCheckIndexesExist($rules));
680
681
        $this->toOneRules = $rules;
682
683
        return $this;
684
    }
685
686
    /**
687
     * @param array $rules
688
     *
689
     * @return self
690
     */
691
    private function setToManyIndexes(array $rules): self
692
    {
693
        assert($this->debugCheckIndexesExist($rules));
694
695
        $this->toManyRules = $rules;
696
697
        return $this;
698
    }
699
700
    /**
701
     * @return int[]
702
     */
703
    protected function getAttributeRules(): array
704
    {
705
        return $this->attributeRules;
706
    }
707
708
    /**
709
     * @return int[]
710
     */
711
    protected function getToOneRules(): array
712
    {
713
        return $this->toOneRules;
714
    }
715
716
    /**
717
     * @return int[]
718
     */
719
    protected function getToManyRules(): array
720
    {
721
        return $this->toManyRules;
722
    }
723
724
    /**
725
     * @return array
726
     */
727
    private function getBlocks(): array
728
    {
729
        return $this->blocks;
730
    }
731
732
    /**
733
     * @return FormatterInterface
734
     */
735 View Code Duplication
    protected function getMessageFormatter(): FormatterInterface
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...
736
    {
737
        if ($this->messageFormatter === null) {
738
            /** @var FormatterFactoryInterface $factory */
739
            $factory                = $this->getContainer()->get(FormatterFactoryInterface::class);
740
            $this->messageFormatter = $factory->createFormatter(static::RESOURCES_NAMESPACE);
741
        }
742
743
        return $this->messageFormatter;
744
    }
745
746
    /**
747
     * @param string $name
748
     *
749
     * @return int
750
     *
751
     * @SuppressWarnings(PHPMD.StaticAccess)
752
     */
753
    private function getAttributeIndex(string $name): int
754
    {
755
        $indexes = JsonApiRuleSerializer::getRulesIndexes($this->getAttributeRules());
756
        $index   = $indexes[$name];
757
758
        return $index;
759
    }
760
761
    /**
762
     * @param string $name
763
     *
764
     * @return bool
765
     *
766
     * @SuppressWarnings(PHPMD.StaticAccess)
767
     */
768
    private function hasAttributeIndex(string $name): bool
769
    {
770
        $indexes      = JsonApiRuleSerializer::getRulesIndexes($this->getAttributeRules());
771
        $hasAttribute = array_key_exists($name, $indexes);
772
773
        return $hasAttribute;
774
    }
775
776
    /**
777
     * @param int   $messageId
778
     * @param array $args
779
     *
780
     * @return string
781
     */
782
    private function formatMessage(int $messageId, array $args = []): string
783
    {
784
        $message = $this->getMessageFormatter()->formatMessage($messageId, $args);
785
786
        return $message;
787
    }
788
789
    /**
790
     * @param array $rules
791
     *
792
     * @return bool
793
     *
794
     * @SuppressWarnings(PHPMD.StaticAccess)
795
     */
796
    private function debugCheckIndexesExist(array $rules): bool
797
    {
798
        $allOk = true;
799
800
        $indexes = array_merge(
801
            JsonApiRuleSerializer::getRulesIndexes($rules),
802
            JsonApiRuleSerializer::getRulesStartIndexes($rules),
803
            JsonApiRuleSerializer::getRulesEndIndexes($rules)
804
        );
805
806
        foreach ($indexes as $index) {
807
            $allOk = $allOk && is_int($index) && JsonApiRuleSerializer::isRuleExist($index, $this->getBlocks());
808
        }
809
810
        return $allOk;
811
    }
812
}
813