Completed
Push — master ( 645898...c389b9 )
by Anton
09:13 queued 06:21
created

ValidatesEntity::getErrors()   B

Complexity

Conditions 5
Paths 2

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 12
nc 2
nop 0
dl 0
loc 23
rs 8.5906
c 0
b 0
f 0
1
<?php
2
/**
3
 * spiral
4
 *
5
 * @author    Wolfy-J
6
 */
7
8
namespace Spiral\Validation;
9
10
use Spiral\Core\Exceptions\ScopeException;
11
use Spiral\Core\Traits\SaturateTrait;
12
use Spiral\Models\DataEntity;
13
use Spiral\Translator\Traits\TranslatorTrait;
14
use Spiral\Translator\Translator;
15
16
/**
17
 * Provides ability to validate mocked data. Model provides ability to localize error messages.
18
 */
19
class ValidatesEntity extends DataEntity
20
{
21
    use SaturateTrait, TranslatorTrait;
22
23
    /**
24
     * Validation rules compatible with ValidatorInterface.
25
     */
26
    const VALIDATES = [];
27
28
    /**
29
     * @var ValidatorInterface
30
     */
31
    private $validator;
32
33
    /**
34
     * Indicates that entity have been validated.
35
     *
36
     * @var bool
37
     */
38
    private $validated = false;
39
40
    /**
41
     * Error cache holds last of errors unless entity value changed.
42
     *
43
     * @var array
44
     */
45
    private $errorsCache = [];
46
47
    /**
48
     * @param array              $data
49
     * @param ValidatorInterface $validator
50
     *
51
     * @throws ScopeException
52
     */
53
    public function __construct(array $data, ValidatorInterface $validator = null)
54
    {
55
        parent::__construct($data);
56
57
        //We always need validator instance, if not provided - resolve vis global scope
58
        $this->setValidator($this->saturate($validator, ValidatorInterface::class));
59
    }
60
61
    /**
62
     * Get entity validator.
63
     *
64
     * @return ValidatorInterface
65
     */
66
    public function getValidator(): ValidatorInterface
67
    {
68
        return $this->validator;
69
    }
70
71
    /**
72
     * Change associated entity validator.
73
     *
74
     * @param ValidatorInterface $validator
75
     * @param bool               $setRules When true entity specific rules to be assigned.
76
     *
77
     * @return ValidatesEntity
78
     */
79
    public function setValidator(
80
        ValidatorInterface $validator,
81
        bool $setRules = true
82
    ): ValidatesEntity {
83
        $this->validator = $validator;
84
85
        if ($setRules) {
86
            $this->validator->setRules(static::VALIDATES);
87
        }
88
89
        return $this;
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95
    public function setField(string $name, $value, bool $filter = true)
96
    {
97
        $this->validated = false;
98
99
        return parent::setField($name, $value, $filter);
100
    }
101
102
    /**
103
     * Check if entity data is valid.
104
     *
105
     * @param string $field
106
     *
107
     * @return bool
108
     */
109
    public function isValid(string $field = null): bool
110
    {
111
        return !$this->hasErrors($field);
112
    }
113
114
    /**
115
     * Check if any of data fields has errors.
116
     *
117
     * @param string $field
118
     *
119
     * @return bool
120
     */
121
    public function hasErrors(string $field = null): bool
122
    {
123
        if (empty($field)) {
124
            return !empty($this->getErrors());
125
        }
126
127
        //Looking for specific error
128
        return !empty($this->getErrors()[$field]);
129
    }
130
131
    /**
132
     * Get validation errors.
133
     *
134
     * @return array
135
     */
136
    public function getErrors(): array
137
    {
138
        $errors = $this->errorsCache;
139
140
        if (!$this->validated) {
141
            $this->validate();
142
143
            $errors = $this->validator->getErrors();
144
            foreach ($errors as &$error) {
145
                if (is_string($error) && Translator::isMessage($error)) {
146
                    //Localizing error message
147
                    $error = $this->say($error);
148
                }
149
150
                unset($error);
151
            }
152
153
            $this->errorsCache = $errors;
154
            $this->validated = true;
155
        }
156
157
        return $this->validateInner($errors);
158
    }
159
160
    /**
161
     * Perform data validation. Method might include custom validations and errors
162
     */
163
    protected function validate()
164
    {
165
        //Configuring validator
166
        $this->validator->setData($this->getFields());
167
168
        //Drop all validation errors set by user
169
        $this->validator->flushRegistered();
170
    }
171
172
    /**
173
     * Validate inner entities.
174
     *
175
     * @param array $errors
176
     *
177
     * @return array
178
     */
179
    private function validateInner(array $errors): array
180
    {
181
        foreach ($this->getFields(false) as $index => $value) {
182
            if (isset($errors[$index])) {
183
                //Invalid on parent level
184
                continue;
185
            }
186
187
            if ($value instanceof ValidatesEntity) {
188
                if (!$value->isValid()) {
189
                    //Nested entities must deal with cache internally
190
                    $errors[$index] = $value->getErrors();
191
                }
192
                continue;
193
            }
194
195
            //Array of nested entities for validation
196
            if (is_array($value) || $value instanceof \Traversable) {
197
                foreach ($value as $nIndex => $nValue) {
198
                    if ($nValue instanceof ValidatesEntity && !$nValue->isValid()) {
199
                        $errors[$index][$nIndex] = $nValue->getErrors();
200
                    }
201
                }
202
            }
203
        }
204
205
        return $errors;
206
    }
207
}