Completed
Push — master ( 442463...d4b8b4 )
by Kristijan
18:40 queued 17:26
created

RulesParser::min()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2.032

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
ccs 4
cts 5
cp 0.8
crap 2.032
1
<?php
2
3
namespace Kris\LaravelFormBuilder;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
use Kris\LaravelFormBuilder\Fields\FormField;
8
9
/**
10
 * Laravel Validator rules to HTML5 attributes parser.
11
 *
12
 * Based on Laravel Validator and Former LiveValidation
13
 * @see https://github.com/laravel/framework
14
 * @see https://github.com/formers/former
15
 */
16
class RulesParser
17
{
18
    /**
19
     * @var FormField
20
     */
21
    protected $field;
22
23
    /**
24
     * @var FormHelper
25
     */
26
    protected $formHelper;
27
28
    /**
29
     * @param FormField $field
30
     */
31 102
    public function __construct(FormField $field)
32
    {
33 102
        $this->field = $field;
34 102
        $this->formHelper = $this->field->getParent()->getFormHelper();
35 102
    }
36
37
    /**
38
     * Parse a rule for an input into an array of attributes.
39
     *
40
     * @param  string|array $rules
41
     * @return array
42
     */
43 6
    public function parse($rules)
44
    {
45 6
        $attributes = array();
46 6
        $rules = $rule = $this->getRulesAsArray($rules);
47
48 6
        foreach ($rules as $rule) {
49 6
            list($rule, $parameters) = $this->parseRule($rule);
50
51 6
            if ($rule && method_exists($this, $rule)) {
52 6
                $attributes += $this->$rule($parameters);
53
            }
54
        }
55
56 6
        return $attributes;
57
    }
58
59
    /**
60
     * Check that a checkbox is accepted. Needs yes, on, 1, or true as value.
61
     *
62
     *   accepted  -> required="required"
63
     *
64
     * @return array
65
     *
66
     * @see http://laravel.com/docs/5.1/validation#rule-accepted
67
     */
68 1
    protected function accepted()
69
    {
70
        return [
71 1
            'required' => 'required',
72 1
            'title' => $this->getTitle('accepted'),
73
        ];
74
    }
75
76
    /**
77
     * Check that the field is required.
78
     *
79
     *   required  -->  required="required"
80
     *
81
     * @return array
82
     *
83
     * @see http://laravel.com/docs/5.1/validation#rule-required
84
     */
85 4
    protected function required()
86
    {
87 4
        return ['required' => 'required'];
88
    }
89
90
    /**
91
     * Check that the input only contains alpha.
92
     *
93
     *   alpha  --> pattern="[a-zA-Z]+"
94
     *
95
     * @return array
96
     */
97 1
    protected function alpha()
98
    {
99
        return [
100 1
            'pattern' => '[a-zA-Z]+',
101 1
            'title' => $this->getTitle('alpha'),
102
        ];
103
    }
104
105
    /**
106
     * Check if the input contains only alpha and num.
107
     *
108
     *   alpha_num  --> pattern="[a-zA-Z0-9]+"
109
     *
110
     * @return array
111
     *
112
     * @see http://laravel.com/docs/5.1/validation#rule-alpha-num
113
     */
114 1
    protected function alphaNum()
115
    {
116
        return [
117 1
            'pattern' => '[a-zA-Z0-9]+',
118 1
            'title' => $this->getTitle('alpha_num'),
119
        ];
120
    }
121
122
    /**
123
     * Check if the input contains only alpha, num and dash.
124
     *
125
     *   alpha_dash  --> pattern="[a-zA-Z0-9_\-]+"
126
     *
127
     * @return array
128
     *
129
     * @see http://laravel.com/docs/5.1/validation#rule-alpha-dash
130
     */
131
    protected function alphaDash()
132
    {
133
        return [
134
            'pattern' => '[a-zA-Z0-9_\-]+',
135
            'title' => $this->getTitle('alpha_dash'),
136
        ];
137
    }
138
139
    /**
140
     * Check if the field is an integer value. Cannot contain decimals.
141
     *
142
     *   integer  --> step="1" (number)
143
     *   integer  --> pattern="\d+" (text)
144
     *
145
     * @return array
146
     *
147
     * @see http://laravel.com/docs/5.1/validation#rule-integer
148
     */
149
    protected function integer()
150
    {
151
        if ($this->isNumeric()) {
152
            return ['step' => 1];
153
        }
154
155
        return [
156
            'pattern' => '\d+',
157
            'title' => $this->getTitle('integer'),
158
        ];
159
    }
160
161
    /**
162
     * Check that a field is numeric. It may contain decimals.
163
     *
164
     *   numeric  --> step="any" (number)
165
     *   numeric  --> pattern="[-+]?[0-9]*[.,]?[0-9]+" (text)
166
     *
167
     * @return array
168
     *
169
     * @see http://laravel.com/docs/5.1/validation#rule-numeric
170
     */
171
    protected function numeric()
172
    {
173
        if ($this->isNumeric()) {
174
            return ['step' => 'any'];
175
        }
176
177
        return [
178
            'pattern' => '[-+]?[0-9]*[.,]?[0-9]+',
179
            'title' => $this->getTitle('numeric'),
180
        ];
181
    }
182
183
    /**
184
     * Check that a value is either 0 or 1, so it can be parsed as bool.
185
     *
186
     *   bool  --> pattern="0|1"
187
     *
188
     * @return array
189
     *
190
     * @see http://laravel.com/docs/5.1/validation#rule-boolean
191
     */
192
    protected function boolean()
193
    {
194
        return [
195
            'pattern' => '0|1',
196
            'title' => $this->getTitle('boolean'),
197
        ];
198
    }
199
200
    /**
201
     * Check that the value is numeric and contains exactly the given digits.
202
     *
203
     *   digits:3  --> min="100" max="999"
204
     *   digits:3  --> pattern="\d{3,5}"  (text)
205
     *
206
     * @param $param
207
     * @return array
208
     *
209
     * @see http://laravel.com/docs/5.1/validation#rule-digits
210
     */
211
    protected function digits($param)
212
    {
213
        $digits = $param[0];
214
215
        if ($this->isNumeric()) {
216
            return [
217
                'min' => pow(10, $digits - 1),
218
                'max' => pow(10, $digits) - 1,
219
            ];
220
        }
221
222
        return [
223
            'pattern' => '\d{' . $digits . '}',
224
            'title' => $this->getTitle('digits', compact('digits')),
225
        ];
226
    }
227
228
    /**
229
     * Check that the value is numeric and contains between min/max digits.
230
     *
231
     *   digits_between:3,5  --> min="100" max="99999"
232
     *   digits_between:3,5  --> pattern="\d{3,5}"  (text)
233
     *
234
     * @param $param
235
     * @return array
236
     *
237
     * @see http://laravel.com/docs/5.1/validation#rule-digits-between
238
     */
239
    protected function digitsBetween($param)
240
    {
241
        list($min, $max) = $param;
242
243
        if ($this->isNumeric()) {
244
            return [
245
                'min' => pow(10, $min - 1),
246
                'max' => pow(10, $max) - 1,
247
            ];
248
        }
249
250
        return [
251
            'pattern' => '\d{' . $min . ',' . $max . '}',
252
            'title' => $this->getTitle('digits_between', compact('min', 'max')),
253
        ];
254
    }
255
256
    /**
257
     * For numbers, set the minimum value.
258
     * For strings, set the minimum number of characters.
259
     *
260
     *   min:5  --> min="5"       (number)
261
     *   min:5  --> minlength="5" (text)
262
     *
263
     * @param $param
264
     * @return array
265
     *
266
     * @see http://laravel.com/docs/5.1/validation#rule-min
267
     */
268 3
    protected function min($param)
269
    {
270 3
        $min = $param[0];
271
272 3
        if ($this->isNumeric()) {
273
            return ['min' => $min];
274
        }
275
276
        return [
277 3
            'minlength' => $min,
278
        ];
279
    }
280
281
    /**
282
     * For numbers, set the max value.
283
     * For strings, set the max number of characters.
284
     *
285
     *   max:5  --> max="5"       (number)
286
     *   max:5  --> maxlength="5" (text)
287
     *
288
     * @param $param
289
     * @return array
290
     *
291
     * @see http://laravel.com/docs/5.1/validation#rule-max
292
     */
293
    protected function max($param)
294
    {
295
        $max = $param[0];
296
297
        if ($this->isNumeric()) {
298
            return ['max' => $max];
299
        }
300
301
        return ['maxlength' => $max];
302
    }
303
304
    /**
305
     * For number/range inputs, check if the number is between the values.
306
     * For strings, check the length of the string.
307
     *
308
     *   between:3,5  --> min="3" max="5"             (number)
309
     *   between:3,5  --> minlength="3" maxlength="5" (text)
310
     *
311
     * @param $param
312
     * @return array
313
     *
314
     * @see http://laravel.com/docs/5.1/validation#rule-between
315
     */
316
    protected function between($param)
317
    {
318
        list($min, $max) = $param;
319
320
        if ($this->isNumeric()) {
321
            return [
322
                'min' => $min,
323
                'max' => $max,
324
            ];
325
        }
326
327
        return [
328
            'minlength' => $min,
329
            'maxlength' => $max,
330
        ];
331
    }
332
333
    /**
334
     * For numbers: Check an exact value.
335
     * For strings: Check the length of the string.
336
     *
337
     *   size:5 --> min="5" max="5" (number)
338
     *   size:5 --> pattern=".{5}"  (text)
339
     *
340
     * @param mixed $param
341
     * @return array
342
     *
343
     * @see http://laravel.com/docs/5.1/validation#rule-size
344
     */
345
    protected function size($param)
346
    {
347
        $size = $param[0];
348
349
        if ($this->isNumeric()) {
350
            return [
351
                'min' => $size,
352
                'max' => $size,
353
                'title' => $this->getTitle('size.numeric', compact('size')),
354
            ];
355
        }
356
357
        return [
358
            'pattern' =>  '.{' . $size . '}',
359
            'title' => $this->getTitle('size.string', compact('size')),
360
        ];
361
    }
362
363
    /**
364
     * Check if the value is one of the give 'in' rule values
365
     * by creating a matching pattern.
366
     *
367
     *   in:foo,bar  --> pattern="foo|bar"
368
     *
369
     * @param array $params
370
     * @return array
371
     *
372
     * @see http://laravel.com/docs/5.1/validation#rule-in
373
     */
374
    protected function in($params)
375
    {
376
        return [
377
            'pattern' => implode('|', $params),
378
            'title' => $this->getTitle('in'),
379
        ];
380
    }
381
382
    /**
383
     * Check if the value is not one of the 'not_in' rule values
384
     * by creating a pattern value.
385
     *
386
     *   not_in:foo,bar  --> pattern="(?:(?!^foo$|^bar$).)*"
387
     *
388
     * @param array $params
389
     * @return array
390
     *
391
     * @see http://laravel.com/docs/5.1/validation#rule-not-in
392
     */
393
    protected function notIn($params)
394
    {
395
        return [
396
            'pattern' => '(?:(?!^' . implode('$|^', $params) . '$).)*',
397
            'title' => $this->getTitle('not_in'),
398
        ];
399
    }
400
401
    /**
402
     * Set the 'min' attribute on a date/datetime/datetime-local field,
403
     * based on the 'before' validation.
404
     *
405
     *   after:01-12-2015 -> min="2015-12-01"
406
     *
407
     * @param  $params
408
     * @return array
409
     *
410
     * @see http://laravel.com/docs/5.1/validation#rule-after
411
     */
412
    protected function after($params)
413
    {
414
        if ($date = $this->getDateAttribute($params[0])) {
415
            return ['min' => $date];
416
        }
417
418
        return [];
419
    }
420
421
    /**
422
     * Set the 'min' attribute on a date/datetime/datetime-local field,
423
     * based on the 'before' validation.
424
     *
425
     *   before:01-12-2015 -> max="2015-12-01"
426
     *
427
     * @param  $params
428
     * @return array
429
     *
430
     * @see http://laravel.com/docs/5.1/validation#rule-before
431
     */
432
    protected function before($params)
433
    {
434
        if ($date = $this->getDateAttribute($params[0])) {
435
            return ['max' => $date];
436
        }
437
438
        return [];
439
    }
440
441
    /**
442
     * Add the image mime-type to a file input.
443
     *
444
     * @return array
445
     *
446
     * @see http://laravel.com/docs/5.1/validation#rule-image
447
     * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept
448
     */
449
    protected function image()
450
    {
451
        return ['accept' => 'image/*'];
452
    }
453
454
    /**
455
     * Add the mime types to the accept attribute.
456
     *
457
     *  mimes:xls,xlsx  --> accept=".xls, .xlsx"
458
     *
459
     * @param  array $params
460
     * @return array
461
     *
462
     * @see http://laravel.com/docs/5.1/validation#rule-mimes
463
     * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept
464
     */
465
    protected function mimes($params)
466
    {
467
        $mimes = '.' . implode(', .', $params);
468
469
        return ['accept'  => $mimes];
470
    }
471
472
    /**
473
     * Get the title, used for validating a rule.
474
     *
475
     * @param  string $rule
476
     * @param  array  $params
477
     * @return string
478
     */
479 3
    protected function getTitle($rule, $params = array())
480
    {
481 3
        $params['attribute'] = $this->field->getOption('label');
482
483 3
        return $this->formHelper->getTranslator()->get('validation.' . $rule, $params);
484
    }
485
486
    /**
487
     * Check if the field is one of certain types.
488
     *
489
     * @param  string|array  $types
490
     * @return bool
491
     */
492 3
    protected function isType($types)
493
    {
494 3
        return in_array($this->field->getType(), (array) $types);
495
    }
496
497
    /**
498
     * Check if the field is numeric.
499
     *
500
     * @return bool
501
     */
502 3
    protected function isNumeric()
503
    {
504 3
        return $this->isType(['number', 'range']);
505
    }
506
507
    /**
508
     * Format a date to the correct format, based on the current field.
509
     *
510
     * @param $dateStr
511
     * @return bool|string
512
     */
513
    protected function getDateAttribute($dateStr)
514
    {
515
        $format = "Y-m-d";
516
        if ($this->isType(['datetime', 'datetime-local'])) {
517
            $format .= '\TH:i:s';
518
        }
519
520
        return date($format, strtotime($dateStr));
521
    }
522
523
    /**
524
     * Methods below are copied from \Illuminate\Validation\Validator
525
     * @see https://github.com/laravel/framework/blob/5.1/src/Illuminate/Validation/Validator.php
526
     * @copyright Taylor Otwell
527
     */
528
529
    /**
530
     * Extract the rule name and parameters from a rule.
531
     *
532
     * @param  array|string $rules
533
     * @return array|null;
0 ignored issues
show
Documentation introduced by
The doc-type array|null; could not be parsed: Expected "|" or "end of type", but got ";" at position 10. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
534
     */
535 6
    protected function parseRule($rules)
536
    {
537 6
        if (is_array($rules)) {
538
            return $this->parseArrayRule($rules);
539
        }
540 6
        if (is_string($rules)) {
541 6
            return $this->parseStringRule($rules);
542
        }
543
    }
544
545
    /**
546
     * Parse an array based rule.
547
     *
548
     * @param  array $rules
549
     * @return array
550
     */
551
    protected function parseArrayRule(array $rules)
552
    {
553
        return [Str::studly(trim(Arr::get($rules, 0))), array_slice($rules, 1)];
554
    }
555
556
    /**
557
     * Parse a string based rule.
558
     *
559
     * @param  string $rules
560
     * @return array
561
     */
562 6
    protected function parseStringRule($rules)
563
    {
564 6
        $parameters = [];
565
        // The format for specifying validation rules and parameters follows an
566
        // easy {rule}:{parameters} formatting convention. For instance the
567
        // rule "Max:3" states that the value may only be three letters.
568 6
        if (strpos($rules, ':') !== false) {
569 3
            list($rules, $parameter) = explode(':', $rules, 2);
570 3
            $parameters = $this->parseParameters($rules, $parameter);
571
        }
572 6
        return [Str::studly(trim($rules)), $parameters];
573
    }
574
575
    /**
576
     * Parse a parameter list.
577
     *
578
     * @param  string $rule
579
     * @param  string $parameter
580
     * @return array
581
     */
582 3
    protected function parseParameters($rule, $parameter)
583
    {
584 3
        if (strtolower($rule) == 'regex') {
585
            return [$parameter];
586
        }
587 3
        return str_getcsv($parameter);
588
    }
589
590
    /**
591
     * Parse field rules as array and initialize closure rules
592
     *
593
     * @param string|array $rules
594
     * @return array
595
     */
596 6
    protected function getRulesAsArray($rules)
597
    {
598 6
        $rulesArray = (is_string($rules)) ? explode('|', $rules) : $rules;
599
600 6
        return array_map(function ($rule) {
601 6
            if ($rule instanceof \Closure) {
602
                return $rule($this->field->getNameKey());
603
            }
604
605 6
            return $rule;
606 6
        }, $rulesArray);
607
    }
608
}
609