Passed
Push — main ( 8d3821...8eb503 )
by Breno
01:47
created

Validator::field()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 1
b 0
f 0
nc 5
nop 2
dl 0
loc 14
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace BrenoRoosevelt\Validation;
5
6
use ReflectionClass;
7
use ReflectionException;
8
9
final class Validator
10
{
11
    /** @var ValidationSet[] */
12
    private array $ruleSets;
13
14
    public static function new(): self
15
    {
16
        return new self;
17
    }
18
19
    public function ruleSet(string $field): ValidationSet
20
    {
21
        foreach ($this->ruleSets as $ruleSet) {
22
            if ($ruleSet->getField() === $field) {
23
                return $ruleSet;
24
            }
25
        }
26
27
        return $this->ruleSets[] = ValidationSet::forField($field);
28
    }
29
30
    public function field(string $field, Validation|ValidationSet ...$rules): self
31
    {
32
        $ruleset = $this->ruleSet($field);
33
        foreach ($rules as $rule) {
34
            if ($rule instanceof Validation) {
35
                $ruleset->add($rule);
36
            }
37
38
            if ($rule instanceof ValidationSet) {
39
                $ruleset->add(...$rule->rules());
40
            }
41
        }
42
43
        return $this;
44
    }
45
46
    public function validate(array $data = []): ValidationResultSet
47
    {
48
        $validationResultSet = new ValidationResultSet;
49
        foreach ($this->ruleSets as $ruleSet) {
50
            $field = $ruleSet->getField();
51
            if (!$this->shouldValidate($ruleSet, $field, $data)) {
52
                continue;
53
            }
54
55
            $result = $ruleSet->validate($data[$field] ?? null, $data);
56
            if (!$result->isOk()) {
57
                $validationResultSet = $validationResultSet->add($result);
58
            }
59
        }
60
61
        return $validationResultSet;
62
    }
63
64
    private function shouldValidate(ValidationSet $ruleSet, string $field, array $data): bool
65
    {
66
        if (!$ruleSet->isRequired() && !array_key_exists($field, $data)) {
67
            return false;
68
        }
69
70
        $value = $data[$field] ?? null;
71
        if ($ruleSet->allowsEmpty() && empty($value)) {
72
            return false;
73
        }
74
75
        return true;
76
    }
77
78
    public function only(string ...$fields): self
79
    {
80
        $instance = clone $this;
81
        $instance->ruleSets =
82
            array_filter($instance->ruleSets, fn(ValidationSet $ruleSet) => in_array($ruleSet->getField(), $fields));
83
        return $instance;
84
    }
85
86
    public function except(string ...$fields): self
87
    {
88
        $instance = clone $this;
89
        $instance->ruleSets =
90
            array_filter($instance->ruleSets, fn(ValidationSet $ruleSet) => !in_array($ruleSet->getField(), $fields));
91
        return $instance;
92
    }
93
94
    /**
95
     * @throws ReflectionException
96
     */
97
    public function validateObject(object $object): ValidationResultSet
98
    {
99
        $data = [];
100
        $class = new ReflectionClass($object);
101
        foreach ($class->getProperties() as $property) {
102
            $data[$property->getName()] = $property->getValue($object);
103
        }
104
105
        $result = Validator::fromProperties($object)->validate($data);
106
107
        foreach ($class->getMethods() as $method) {
108
            $ruleSet = ValidationSet::fromReflectionMethod($method);
109
            $value = $method->invoke($method->isStatic() ? null : $object);
110
            $methodResult = $ruleSet->validate($value);
111
            if (!$methodResult->isOk()) {
112
                $result = $result->add($methodResult);
113
            }
114
        }
115
116
        return $result;
117
    }
118
119
    /**
120
     * @param array $data
121
     * @param ?string $message
122
     * @return void
123
     * @throws ValidationException
124
     */
125
    public function validateOrFail(array $data = [], ?string $message = null)
126
    {
127
        $result = $this->validate($data);
128
        if (!$result->isOk()) {
129
            throw new ValidationException($result, $message);
130
        }
131
    }
132
133
    /**
134
     * @param string|object $objectOrClass
135
     * @param int|null $filter filter properties, ex: ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PRIVATE
136
     * @return static
137
     * @throws ReflectionException if the class does not exist
138
     */
139
    public static function fromProperties(string|object $objectOrClass, ?int $filter = null): self
140
    {
141
        $instance = new self;
142
        $instance->ruleSets = ValidationSet::fromProperties($objectOrClass, $filter);
143
        return $instance;
144
    }
145
}
146