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

Nested::getOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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