Passed
Pull Request — master (#98)
by Dmitriy
02:10
created

Nested   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 88
Duplicated Lines 0 %

Test Coverage

Coverage 95.83%

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 19
eloc 45
c 2
b 1
f 0
dl 0
loc 88
ccs 46
cts 48
cp 0.9583
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getOptions() 0 3 1
A errorWhenPropertyPathIsNotFound() 0 5 1
B validateValue() 0 36 9
A __construct() 0 13 4
A checkRules() 0 6 3
A propertyPathIsNotFoundMessage() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Rule;
6
7
use InvalidArgumentException;
8
use Traversable;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Validator\DataSetInterface;
11
use Yiisoft\Validator\Result;
12
use Yiisoft\Validator\Rule;
13
use Yiisoft\Validator\Rules;
14
15
/**
16
 * Each validator validates an array by checking each of its elements against a set of rules
17
 */
18
class Nested extends Rule
19
{
20
    /**
21
     * @var Rule[][]
22
     */
23
    private iterable $rules;
24
25
    private bool $errorWhenPropertyPathIsNotFound = false;
26
    private string $propertyPathIsNotFoundMessage = 'Property path "{path}" is not found.';
27
28 11
    public function __construct(iterable $rules)
29
    {
30 11
        $rules = $rules instanceof Traversable ? iterator_to_array($rules) : (array)$rules;
31 11
        if (empty($rules)) {
32 1
            throw new InvalidArgumentException('Rules should not be empty.');
33
        }
34 10
        if ($this->checkRules($rules)) {
35 1
            throw new InvalidArgumentException(sprintf(
36 1
                'Each rule should be instance of %s.',
37 1
                Rule::class
38
            ));
39
        }
40 9
        $this->rules = $rules;
41 9
    }
42
43 8
    protected function validateValue($value, DataSetInterface $dataSet = null): Result
44
    {
45 8
        $result = new Result();
46 8
        if (!is_object($value) && !is_array($value)) {
47 1
            $result->addError(sprintf(
48 1
                'Value should be an array or an object. %s given',
49 1
                gettype($value)
50
            ));
51 1
            return $result;
52
        }
53 7
        $value = (array) $value;
54
55 7
        foreach ($this->rules as $valuePath => $rules) {
56 7
            $rulesSet = is_array($rules) ? $rules : [$rules];
57 7
            if ($this->errorWhenPropertyPathIsNotFound && !ArrayHelper::pathExists($value, $valuePath)) {
58 2
                $result->addError(
59 2
                    $this->translateMessage(
60 2
                        $this->propertyPathIsNotFoundMessage,
61
                        [
62 2
                            'path' => $valuePath,
63
                        ]
64
                    )
65
                );
66 2
                continue;
67
            }
68 5
            $validatedValue = ArrayHelper::getValueByPath($value, $valuePath);
69 5
            $aggregateRule = new Rules($rulesSet);
70 5
            $itemResult = $aggregateRule->validate($validatedValue);
71 5
            if ($itemResult->isValid() === false) {
72 3
                foreach ($itemResult->getErrors() as $error) {
73 3
                    $result->addError($error);
74
                }
75
            }
76
        }
77
78 7
        return $result;
79
    }
80
81 2
    public function errorWhenPropertyPathIsNotFound(bool $value): self
82
    {
83 2
        $new = clone $this;
84 2
        $new->errorWhenPropertyPathIsNotFound = $value;
85 2
        return $new;
86
    }
87
88 1
    public function propertyPathIsNotFoundMessage(string $message): self
89
    {
90 1
        $new = clone $this;
91 1
        $new->propertyPathIsNotFoundMessage = $message;
92 1
        return $new;
93
    }
94
95
    public function getOptions(): array
96
    {
97
        return $this->rules->asArray();
98
    }
99
100 10
    private function checkRules(array $rules): bool
101
    {
102 10
        return array_reduce(
103 10
            $rules,
104 10
            fn (bool $carry, $rule) => $carry || is_array($rule) ? $this->checkRules($rule) : !$rule instanceof Rule,
105 10
            false
106
        );
107
    }
108
}
109