Completed
Branch develop (c2aa4c)
by Anton
05:17
created

ValidatorTrait::validator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4286
cc 2
eloc 4
nc 2
nop 0
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
namespace Spiral\Validation\Traits;
9
10
use Interop\Container\ContainerInterface;
11
use Spiral\Core\Exceptions\SugarException;
12
use Spiral\Core\FactoryInterface;
13
use Spiral\Events\Traits\EventsTrait;
14
use Spiral\Translator\Traits\TranslatorTrait;
15
use Spiral\Translator\TranslatorInterface;
16
use Spiral\Validation\Events\ValidatedEvent;
17
use Spiral\Validation\Events\ValidatorEvent;
18
use Spiral\Validation\Exceptions\ValidationException;
19
use Spiral\Validation\ValidatorInterface;
20
21
/**
22
 * Provides set of common validation methods.
23
 */
24
trait ValidatorTrait
25
{
26
    /**
27
     * Will translate every message it [[]] used and fire some events.
28
     */
29
    use TranslatorTrait, EventsTrait;
30
31
    /**
32
     * @var ValidatorInterface
33
     */
34
    private $validator = null;
35
36
    /**
37
     * @whatif private
38
     * @var array
39
     */
40
    protected $errors = [];
41
42
    /**
43
     * Fields (data) to be validated. Named like that for convenience.
44
     *
45
     * @whatif private
46
     * @var array
47
     */
48
    protected $fields = [];
49
50
    /**
51
     * Validation rules defined in validator format. Named like that for convenience.
52
     *
53
     * @var array
54
     */
55
    protected $validates = [];
56
57
    /**
58
     * Attach custom validator to model.
59
     *
60
     * @param ValidatorInterface $validator
61
     */
62
    public function setValidator(ValidatorInterface $validator)
63
    {
64
        $this->validator = $validator;
65
    }
66
67
    /**
68
     * Check if context data is valid.
69
     *
70
     * @return bool
71
     */
72
    public function isValid()
73
    {
74
        $this->validate();
75
76
        return empty($this->errors);
77
    }
78
79
    /**
80
     * Check if context data has errors.
81
     *
82
     * @return bool
83
     */
84
    public function hasErrors()
85
    {
86
        return !$this->isValid();
87
    }
88
89
    /**
90
     * List of errors associated with parent field, every field should have only one error assigned.
91
     *
92
     * @param bool $reset Re-validate object.
93
     * @return array
94
     */
95
    public function getErrors($reset = false)
96
    {
97
        $this->validate($reset);
98
99
        $errors = [];
100
        foreach ($this->errors as $field => $error) {
101
            if (
102
                is_string($error)
103
                && substr($error, 0, 2) == TranslatorInterface::I18N_PREFIX
104
                && substr($error, -2) == TranslatorInterface::I18N_POSTFIX
105
            ) {
106
                //We will localize only messages embraced with [[ and ]]
107
                $error = $this->say($error);
108
            }
109
110
            $errors[$field] = $error;
111
        }
112
113
        return $errors;
114
    }
115
116
    /**
117
     * Get associated instance of validator or return new one.
118
     *
119
     * @return ValidatorInterface
120
     * @throws SugarException
121
     */
122
    protected function validator()
123
    {
124
        if (!empty($this->validator)) {
125
            return $this->validator;
126
        }
127
128
        return $this->validator = $this->createValidator();
129
    }
130
131
    /**
132
     * Validate data using associated validator.
133
     *
134
     * @param bool $reset Implementation might reset validation if needed.
135
     * @return bool
136
     * @throws ValidationException
137
     * @throws SugarException
138
     * @event validated(ValidatedEvent)
139
     */
140
    protected function validate($reset = false)
0 ignored issues
show
Unused Code introduced by
The parameter $reset is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
141
    {
142
        //Refreshing validation fields
143
        $this->validator()->setData($this->fields);
144
145
        /**
146
         * @var ValidatedEvent $validatedEvent
147
         */
148
        $validatedEvent = $this->dispatch('validated', new ValidatedEvent(
149
            $this->validator()->getErrors()
150
        ));
151
152
        //Collecting errors if any
153
        return empty($this->errors = $validatedEvent->getErrors());
154
    }
155
156
    /**
157
     * Attach error to data field. Internal method to be used in validations.
158
     *
159
     * @param string $field
160
     * @param string $message
161
     */
162
    protected function setError($field, $message)
163
    {
164
        $this->errors[$field] = $message;
165
    }
166
167
    /**
168
     * Check if desired field caused some validation error.
169
     *
170
     * @param string $field
171
     * @return bool
172
     */
173
    protected function hasError($field)
174
    {
175
        return !empty($this->errors[$field]);
176
    }
177
178
    /**
179
     * Create instance of ValidatorInterface.
180
     *
181
     * @param array              $rules     Non empty rules will initiate validator.
182
     * @param ContainerInterface $container Will fall back to global container.
183
     * @return ValidatorInterface
184
     * @throws SugarException
185
     * @event validator(ValidatorEvent)
186
     */
187
    protected function createValidator(
188
        array $rules = [],
189
        ContainerInterface $container = null
190
    ) {
191
        if (empty($container)) {
192
            $container = $this->container();
193
        }
194
195
        if (empty($container) || !$container->has(ValidatorInterface::class)) {
196
            //We can't create default validation without any rule, this is not secure
197
            throw new SugarException(
198
                "Unable to create Validator, no global container set or binding is missing."
199
            );
200
        }
201
202
        //We need constructor
203
        if ($container instanceof FactoryInterface) {
204
            $constructor = $container;
205
        } else {
206
            $constructor = $container->get(FactoryInterface::class);
207
        }
208
209
        //Receiving instance of validator from container
210
        $validator = $constructor->construct(ValidatorInterface::class, [
211
            'data'  => $this->fields,
212
            'rules' => !empty($rules) ? $rules : $this->validates
213
        ]);
214
215
        /**
216
         * @var ValidatorEvent $validatorEvent
217
         */
218
        $validatorEvent = $this->dispatch('validator', new ValidatorEvent($validator));
219
220
        return $validatorEvent->validator();
221
    }
222
}