Completed
Pull Request — master (#209)
by
unknown
14:15
created

RulesParser::containsRequired()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2

Importance

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