Passed
Pull Request — master (#364)
by
unknown
02:45
created

NestedHandler::validate()   D

Complexity

Conditions 20
Paths 95

Size

Total Lines 103
Code Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 49
CRAP Score 20.024

Importance

Changes 7
Bugs 1 Features 0
Metric Value
eloc 57
c 7
b 1
f 0
dl 0
loc 103
ccs 49
cts 51
cp 0.9608
rs 4.1666
cc 20
nc 95
nop 3
crap 20.024

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
                    array_push(/** @scrutinizer ignore-type */ $valuePathList, ...$error->getValuePath());
Loading history...
136
                }
137
138 19
                $result->addError($error->getMessage(), $error->getParameters(), $valuePathList);
139
            }
140 19
            $results[] = $result;
141
        }
142
143 29
        foreach ($results as $result) {
144 19
            foreach ($result->getErrors() as $error) {
145 19
                $compoundResult->addError($error->getMessage(), $error->getParameters(), $error->getValuePath());
146
            }
147
        }
148
149 29
        return $compoundResult;
150
    }
151
}
152