Passed
Push — main ( 859bf8...43bcd8 )
by Breno
01:52
created

RuleSet::add()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 5
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace BrenoRoosevelt\Validation;
5
6
use BrenoRoosevelt\Validation\Exception\ValidateOrFail;
7
use BrenoRoosevelt\Validation\Rules\AllowsEmpty;
8
use BrenoRoosevelt\Validation\Rules\AllowsNull;
9
use BrenoRoosevelt\Validation\Rules\IsEmpty;
10
use BrenoRoosevelt\Validation\Rules\NotRequired;
11
12
class RuleSet implements Rule
13
{
14
    use RuleChain,
15
        ValidateOrFail,
16
        BelongsToField;
17
18
    private array $rules = [];
19
20
    final public function __construct(?string $field = null, Rule | RuleSet ...$rules)
21
    {
22
        $this->attachRules(...$rules);
23
        $this->setField($field);
24
    }
25
26
    public static function new(): self
27
    {
28
        return new self;
29
    }
30
31
    public static function of(string $field, Rule | RuleSet ...$rules): self
32
    {
33
        return new self($field, ...$rules);
34
    }
35
36
    public static function withRules(Rule | RuleSet ...$rules): self
37
    {
38
        return new self(null, ...$rules);
39
    }
40
41
    public function add(Rule | RuleSet ...$rules): static
42
    {
43
        $instance = clone $this;
44
        $instance->attachRules(...$rules);
45
        return $instance;
46
    }
47
48
    private function attachRules(Rule | RuleSet ...$rules): void
49
    {
50
        foreach ($rules as $ruleOrRuleSet) {
51
            array_push(
52
                $this->rules,
53
                ...($ruleOrRuleSet instanceof Rule ? [$ruleOrRuleSet] : $ruleOrRuleSet->rules())
54
            );
55
        }
56
    }
57
58
    public function validate(mixed $input, array $context = []): ValidationResult
59
    {
60
        $result = $empty = $this->newEmptyResult();
61
        if (!$this->shouldValidate($input)) {
62
            return $empty;
63
        }
64
65
        foreach ($this->rules as $rule) {
66
            $result = $result->addError(...$rule->validate($input, $context)->getErrors());
67
        }
68
69
        return $result;
70
    }
71
72
    private function shouldValidate(mixed $input): bool
73
    {
74
        if (null === $input && $this->isAllowsNull()) {
75
            return false;
76
        }
77
78
        $isEmptyInput = (new IsEmpty)->isValid($input);
79
        if ($isEmptyInput && $this->isAllowsEmpty()) {
80
            return false;
81
        }
82
83
        return true;
84
    }
85
86
    public function hasRule(string $ruleClass): bool
87
    {
88
        if (!class_exists($ruleClass)) {
89
            return false;
90
        }
91
92
        foreach ($this->rules as $rule) {
93
            if ($rule instanceof $ruleClass) {
94
                return true;
95
            }
96
        }
97
98
        return false;
99
    }
100
101
    public function isNotRequired(): bool
102
    {
103
        return $this->hasRule(NotRequired::class);
104
    }
105
106
    public function isAllowsEmpty(): bool
107
    {
108
        return $this->isEmpty() || $this->hasRule(AllowsEmpty::class);
109
    }
110
111
    public function isAllowsNull(): bool
112
    {
113
        return $this->isEmpty() || $this->hasRule(AllowsNull::class);
114
    }
115
116
    public function isEmpty(): bool
117
    {
118
        return empty($this->rules);
119
    }
120
121
    /** @return Rule[] */
122
    public function rules(): array
123
    {
124
        return $this->rules;
125
    }
126
}
127