Passed
Pull Request — master (#265)
by Dmitriy
02:23
created

NestedHandler::validate()   D

Complexity

Conditions 17
Paths 160

Size

Total Lines 84
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 44
CRAP Score 17

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 47
c 1
b 0
f 0
dl 0
loc 84
ccs 44
cts 44
cp 1
rs 4.7166
cc 17
nc 160
nop 3
crap 17

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use Generator;
8
use InvalidArgumentException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Strings\StringHelper;
11
use Yiisoft\Translator\TranslatorInterface;
12
use Yiisoft\Validator\DataSet\ObjectDataSet;
13
use Yiisoft\Validator\Exception\UnexpectedRuleException;
14
use Yiisoft\Validator\Result;
15
use Yiisoft\Validator\RuleHandlerInterface;
16
use Yiisoft\Validator\ValidationContext;
17
18
use function is_array;
19
use function is_int;
20
use function is_object;
21
22
/**
23
 * Can be used for validation of nested structures.
24
 *
25
 * For example, we have an inbound request with the following structure:
26
 *
27
 * ```php
28
 * $request = [
29
 *     'author' => [
30
 *         'name' => 'Dmitry',
31
 *         'age' => 18,
32
 *     ],
33
 * ];
34
 * ```
35
 *
36
 * So to make validation we can configure it like this:
37
 *
38
 * ```php
39
 * $rule = new Nested([
40
 *     'author' => new Nested([
41
 *         'name' => [new HasLength(min: 3)],
42
 *         'age' => [new Number(min: 18)],
43
 *     )];
44
 * ]);
45
 * ```
46
 */
47
final class NestedHandler implements RuleHandlerInterface
48
{
49 39
    public function __construct(private TranslatorInterface $translator)
50
    {
51
    }
52
53 39
    public function validate(mixed $value, object $rule, ValidationContext $context): Result
54
    {
55 39
        if (!$rule instanceof Nested) {
56 1
            throw new UnexpectedRuleException(Nested::class, $rule);
57
        }
58
59 38
        if ($rule->getRules() === null) {
60 8
            if (!is_object($value)) {
61 3
                throw new InvalidArgumentException(
62 3
                    sprintf(
63
                        'Nested rule without rules could be used for objects only. %s given.',
64 3
                        get_debug_type($value)
65
                    )
66
                );
67
            }
68
69 5
            $dataSet = new ObjectDataSet($value, $rule->getPropertyVisibility());
70
71 5
            return $context->getValidator()->validate($dataSet);
72
        }
73
74 30
        if (is_array($value)) {
75 27
            $data = $value;
76 3
        } elseif (is_object($value)) {
77 1
            $data = (new ObjectDataSet($value, $rule->getPropertyVisibility()))->getData();
78
        } else {
79 2
            $message = sprintf(
80
                'Value should be an array or an object. %s given.',
81 2
                get_debug_type($value)
82
            );
83 2
            $translatedMessage = $this->translator->translate(
84
                $message,
85 2
                ['attribute' => $context->getAttribute(), 'value' => $value]
86
            );
87 2
            return (new Result())->addError($translatedMessage);
88
        }
89
90 28
        $compoundResult = new Result();
91 28
        $results = [];
92 28
        foreach ($rule->getRules() as $valuePath => $rules) {
93 27
            if ($rule->getRequirePropertyPath() && !ArrayHelper::pathExists($data, $valuePath)) {
94 3
                $translatedMessage = $this->translator->translate(
95 3
                    $rule->getNoPropertyPathMessage(),
96 3
                    ['path' => $valuePath, 'attribute' => $context->getAttribute(), 'value' => $data]
97
                );
98
                /**
99
                 * @psalm-suppress InvalidScalarArgument
100
                 */
101 3
                $compoundResult->addError($translatedMessage, StringHelper::parsePath($valuePath));
102
103 3
                continue;
104
            }
105
106 24
            $validatedValue = ArrayHelper::getValueByPath($data, $valuePath);
107 24
            $rules = is_array($rules) || $rules instanceof Generator ? $rules : [$rules];
108
109 24
            $itemResult = $context->getValidator()->validate($validatedValue, $rules);
110
111 24
            if ($itemResult->isValid()) {
112 9
                continue;
113
            }
114
115 19
            $result = new Result();
116
117 19
            foreach ($itemResult->getErrors() as $error) {
118 19
                $errorValuePath = is_int($valuePath) ? [$valuePath] : StringHelper::parsePath($valuePath);
119 19
                if (!empty($error->getValuePath())) {
120 8
                    array_push($errorValuePath, ...$error->getValuePath());
121
                }
122
                /**
123
                 * @psalm-suppress InvalidScalarArgument
124
                 */
125 19
                $result->addError($error->getMessage(), $errorValuePath);
126
            }
127 19
            $results[] = $result;
128
        }
129
130 28
        foreach ($results as $result) {
131 19
            foreach ($result->getErrors() as $error) {
132 19
                $compoundResult->addError($error->getMessage(), $error->getValuePath());
133
            }
134
        }
135
136 28
        return $compoundResult;
137
    }
138
}
139