Passed
Push — master ( 6fa460...6bbb33 )
by Paul
18:19 queued 07:35
created

Validator::setRules()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 5.0342

Importance

Changes 0
Metric Value
eloc 8
c 0
b 0
f 0
dl 0
loc 13
ccs 8
cts 9
cp 0.8889
rs 9.6111
cc 5
nc 5
nop 1
crap 5.0342
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Modules;
4
5
use BadMethodCallException;
6
use GeminiLabs\SiteReviews\Defaults\ValidationStringsDefaults;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Helpers\Str;
9
use GeminiLabs\SiteReviews\Modules\Validator\ValidationRules;
10
11
/**
12
 * @see \Illuminate\Validation\Validator (5.3)
13
 */
14
class Validator
15
{
16
    use ValidationRules;
17
18
    /**
19
     * @var array
20
     */
21
    public $errors = [];
22
23
    /**
24
     * The data under validation.
25
     * @var array
26
     */
27
    protected $data = [];
28
29
    /**
30
     * The failed validation rules.
31
     * @var array
32
     */
33
    protected $failedRules = [];
34
35
    /**
36
     * The rules to be applied to the data.
37
     * @var array
38
     */
39
    protected $rules = [];
40
41
    /**
42
     * The size related validation rules.
43
     * @var array
44
     */
45
    protected $sizeRules = [
46
        'Between', 'Max', 'Min',
47
    ];
48
49
    /**
50
     * The validation rules that imply the field is required.
51
     * @var array
52
     */
53
    protected $implicitRules = [
54
        'Required',
55
    ];
56
57
    /**
58
     * The numeric related validation rules.
59
     * @var array
60
     */
61
    protected $numericRules = [
62
        'Number',
63
    ];
64
65
    /**
66
     * Run the validator's rules against its data.
67
     * @param mixed $data
68
     * @return array
69
     */
70 3
    public function validate($data, array $rules = [])
71
    {
72 3
        $this->normalizeData($data);
73 3
        $this->setRules($rules);
74 3
        foreach ($this->rules as $attribute => $rules) {
75 3
            foreach ($rules as $rule) {
76 3
                $this->validateAttribute($attribute, $rule);
77 3
                if ($this->shouldStopValidating($attribute)) {
78 2
                    break;
79
                }
80
            }
81
        }
82 3
        return $this->errors;
83
    }
84
85
    /**
86
     * Validate a given attribute against a rule.
87
     * @param string $attribute
88
     * @param string $rule
89
     * @return void
90
     * @throws BadMethodCallException
91
     */
92 3
    public function validateAttribute($attribute, $rule)
93
    {
94 3
        list($rule, $parameters) = $this->parseRule($rule);
95 3
        if ('' == $rule) {
96
            return;
97
        }
98 3
        $value = $this->getValue($attribute);
99 3
        $method = Helper::buildMethodName($rule, 'validate');
100 3
        if (!method_exists($this, $method)) {
101
            throw new BadMethodCallException("Method [$method] does not exist.");
102
        }
103 3
        if (!$this->$method($value, $attribute, $parameters)) {
104 2
            $this->addFailure($attribute, $rule, $parameters);
105
        }
106 3
    }
107
108
    /**
109
     * Add an error message to the validator's collection of errors.
110
     * @param string $attribute
111
     * @param string $rule
112
     * @return void
113
     */
114 2
    protected function addError($attribute, $rule, array $parameters)
115
    {
116 2
        $message = $this->getMessage($attribute, $rule, $parameters);
117 2
        $this->errors[$attribute][] = $message;
118 2
    }
119
120
    /**
121
     * Add a failed rule and error message to the collection.
122
     * @param string $attribute
123
     * @param string $rule
124
     * @return void
125
     */
126 2
    protected function addFailure($attribute, $rule, array $parameters)
127
    {
128 2
        $this->addError($attribute, $rule, $parameters);
129 2
        $this->failedRules[$attribute][$rule] = $parameters;
130 2
    }
131
132
    /**
133
     * Get the data type of the given attribute.
134
     * @param string $attribute
135
     * @return string
136
     */
137
    protected function getAttributeType($attribute)
138
    {
139
        return !$this->hasRule($attribute, $this->numericRules)
140
            ? 'length'
141
            : '';
142
    }
143
144
    /**
145
     * Get the validation message for an attribute and rule.
146
     * @param string $attribute
147
     * @param string $rule
148
     * @return string|null
149
     */
150 2
    protected function getMessage($attribute, $rule, array $parameters)
151
    {
152 2
        if (in_array($rule, $this->sizeRules)) {
153
            return $this->getSizeMessage($attribute, $rule, $parameters);
154
        }
155 2
        $lowerRule = Str::snakeCase($rule);
156 2
        return $this->translator($lowerRule, $parameters);
157
    }
158
159
    /**
160
     * Get a rule and its parameters for a given attribute.
161
     * @param string $attribute
162
     * @param string|array $rules
163
     * @return array|null|void
164
     */
165 3
    protected function getRule($attribute, $rules)
166
    {
167 3
        if (!array_key_exists($attribute, $this->rules)) {
168
            return;
169
        }
170 3
        $rules = (array) $rules;
171 3
        foreach ($this->rules[$attribute] as $rule) {
172 3
            list($rule, $parameters) = $this->parseRule($rule);
173 3
            if (in_array($rule, $rules)) {
174 3
                return [$rule, $parameters];
175
            }
176
        }
177 3
    }
178
179
    /**
180
     * Get the size of an attribute.
181
     * @param string $attribute
182
     * @param mixed $value
183
     * @return mixed
184
     */
185 2
    protected function getSize($attribute, $value)
186
    {
187 2
        $hasNumeric = $this->hasRule($attribute, $this->numericRules);
188 2
        if (is_numeric($value) && $hasNumeric) {
189
            return $value;
190 2
        } elseif (is_array($value)) {
191
            return count($value);
192
        }
193 2
        return mb_strlen($value);
194
    }
195
196
    /**
197
     * Get the proper error message for an attribute and size rule.
198
     * @param string $attribute
199
     * @param string $rule
200
     * @return string|null
201
     */
202
    protected function getSizeMessage($attribute, $rule, array $parameters)
203
    {
204
        $type = $this->getAttributeType($attribute);
205
        $lowerRule = Str::snakeCase($rule.$type);
206
        return $this->translator($lowerRule, $parameters);
207
    }
208
209
    /**
210
     * Get the value of a given attribute.
211
     * @param string $attribute
212
     * @return mixed
213
     */
214 3
    protected function getValue($attribute)
215
    {
216 3
        if (isset($this->data[$attribute])) {
217 3
            return $this->data[$attribute];
218
        }
219
    }
220
221
    /**
222
     * Determine if the given attribute has a rule in the given set.
223
     * @param string $attribute
224
     * @param string|array $rules
225
     * @return bool
226
     */
227 3
    protected function hasRule($attribute, $rules)
228
    {
229 3
        return !is_null($this->getRule($attribute, $rules));
230
    }
231
232
    /**
233
     * Normalize the provided data to an array.
234
     * @param mixed $data
235
     * @return void
236
     */
237 3
    protected function normalizeData($data)
238
    {
239 3
        $this->data = is_object($data)
240
            ? get_object_vars($data)
241 3
            : $data;
242 3
    }
243
244
    /**
245
     * Parse a parameter list.
246
     * @param string $rule
247
     * @param string $parameter
248
     * @return array
249
     */
250 2
    protected function parseParameters($rule, $parameter)
251
    {
252 2
        return 'regex' == strtolower($rule)
253
            ? [$parameter]
254 2
            : str_getcsv($parameter);
255
    }
256
257
    /**
258
     * Extract the rule name and parameters from a rule.
259
     * @param string $rule
260
     * @return array
261
     */
262 3
    protected function parseRule($rule)
263
    {
264 3
        $parameters = [];
265 3
        if (Str::contains(':', $rule)) {
266 2
            list($rule, $parameter) = explode(':', $rule, 2);
267 2
            $parameters = $this->parseParameters($rule, $parameter);
268
        }
269 3
        $rule = Str::camelCase($rule);
270 3
        return [$rule, $parameters];
271
    }
272
273
    /**
274
     * Set the validation rules.
275
     * @return void
276
     */
277 3
    protected function setRules(array $rules)
278
    {
279 3
        foreach ($rules as $key => $rule) {
280 3
            $validationRules = is_string($rule)
281 3
                ? explode('|', $rule)
282 3
                : $rule;
283
            // unset rules if the attribute is not required and the value is an empty string
284 3
            if (empty(array_intersect(['accepted','required'], $validationRules)) && '' === $this->getValue($key)) {
285
                $validationRules = [];
286
            }
287 3
            $rules[$key] = $validationRules;
288
        }
289 3
        $this->rules = $rules;
290 3
    }
291
292
    /**
293
     * Check if we should stop further validations on a given attribute.
294
     * @param string $attribute
295
     * @return bool
296
     */
297 3
    protected function shouldStopValidating($attribute)
298
    {
299 3
        return $this->hasRule($attribute, $this->implicitRules)
300 3
            && isset($this->failedRules[$attribute])
301 3
            && array_intersect(array_keys($this->failedRules[$attribute]), $this->implicitRules);
302
    }
303
304
    /**
305
     * Returns a translated message for the attribute.
306
     * @param string $key
307
     * @return void|string
308
     */
309 2
    protected function translator($key, array $parameters)
310
    {
311 2
        $strings = glsr(ValidationStringsDefaults::class)->defaults();
312 2
        if (isset($strings[$key])) {
313 2
            return $this->replace($strings[$key], $parameters);
314
        }
315
        return 'error';
316
    }
317
}
318