Completed
Pull Request — master (#208)
by
unknown
14:08
created

FormField::getValidationRules()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2.0117

Importance

Changes 1
Bugs 0 Features 1
Metric Value
dl 0
loc 14
ccs 6
cts 7
cp 0.8571
rs 9.4286
c 1
b 0
f 1
cc 2
eloc 8
nc 2
nop 0
crap 2.0117
1
<?php  namespace Kris\LaravelFormBuilder\Fields;
2
3
use Illuminate\Database\Eloquent\Collection;
4
use Illuminate\Database\Eloquent\Model;
5
use Kris\LaravelFormBuilder\Form;
6
use Kris\LaravelFormBuilder\FormHelper;
7
use Kris\LaravelFormBuilder\RulesParser;
8
9
/**
10
 * Class FormField
11
 *
12
 * @package Kris\LaravelFormBuilder\Fields
13
 */
14
abstract class FormField
15
{
16
    /**
17
     * Name of the field
18
     *
19
     * @var
20
     */
21
    protected $name;
22
23
    /**
24
     * Type of the field
25
     *
26
     * @var
27
     */
28
    protected $type;
29
30
    /**
31
     * All options for the field
32
     *
33
     * @var
34
     */
35
    protected $options = [];
36
37
    /**
38
     * Is field rendered
39
     *
40
     * @var bool
41
     */
42
    protected $rendered = false;
43
44
    /**
45
     * @var Form
46
     */
47
    protected $parent;
48
49
    /**
50
     * @var string
51
     */
52
    protected $template;
53
54
    /**
55
     * @var FormHelper
56
     */
57
    protected $formHelper;
58
59
    /**
60
     * Name of the property for value setting
61
     *
62
     * @var string
63
     */
64
    protected $valueProperty = 'value';
65
66
    /**
67
     * Name of the property for default value
68
     *
69
     * @var string
70
     */
71
    protected $defaultValueProperty = 'default_value';
72
73
    /**
74
     * Is default value set?
75
     * @var bool
76
     */
77
    protected $hasDefault = false;
78
79
    /**
80
     * @var \Closure|null
81
     */
82
    protected $valueClosure = null;
83
84
    /**
85
     * @param             $name
86
     * @param             $type
87
     * @param Form        $parent
88
     * @param array       $options
89
     */
90 62
    public function __construct($name, $type, Form $parent, array $options = [])
91
    {
92 62
        $this->name = $name;
93 62
        $this->type = $type;
94 62
        $this->parent = $parent;
95 62
        $this->formHelper = $this->parent->getFormHelper();
96 62
        $this->setTemplate();
97 62
        $this->setDefaultOptions($options);
98 62
        $this->setupValue();
99 57
    }
100
101 62
    protected function setupValue()
102
    {
103 62
        $value = $this->getOption($this->valueProperty);
104 62
        $isChild = $this->getOption('is_child');
105
106 62
        if ($value instanceof \Closure) {
107
            $this->valueClosure = $value;
108
        }
109
110 62
        if (($value === null || $value instanceof \Closure) && !$isChild) {
111 53
            $this->setValue($this->getModelValueAttribute($this->parent->getModel(), $this->name));
112 18
        } elseif (!$isChild) {
113 12
            $this->hasDefault = true;
114
        }
115 57
    }
116
117
    /**
118
     * Get the template, can be config variable or view path
119
     *
120
     * @return string
121
     */
122
    abstract protected function getTemplate();
123
124
    /**
125
     * @return string
126
     */
127 24
    protected function getViewTemplate()
128
    {
129 24
        return $this->parent->getTemplatePrefix() . $this->getOption('template', $this->template);
130
    }
131
132
    /**
133
     * @param array $options
134
     * @param bool  $showLabel
135
     * @param bool  $showField
136
     * @param bool  $showError
137
     * @return string
138
     */
139 24
    public function render(array $options = [], $showLabel = true, $showField = true, $showError = true)
140
    {
141 24
        $this->prepareOptions($options);
142 24
        $value = $this->getValue();
143 24
        $defaultValue = $this->getDefaultValue();
144
145 24
        if ($showField) {
146 24
            $this->rendered = true;
147
        }
148
149
        // Override default value with value
150 24
        if (!$this->isValidValue($value) && $this->isValidValue($defaultValue)) {
151
            $this->setOption($this->valueProperty, $defaultValue);
152
        }
153
154 24
        if (!$this->needsLabel()) {
155 6
            $showLabel = false;
156
        }
157
158 24
        if ($showError) {
159 23
            $showError = $this->parent->haveErrorsEnabled();
160
        }
161
162 24
        return $this->formHelper->getView()->make(
163 24
            $this->getViewTemplate(),
164
            [
165 24
                'name' => $this->name,
166 24
                'nameKey' => $this->getNameKey(),
167 24
                'type' => $this->type,
168 24
                'options' => $this->options,
169 24
                'showLabel' => $showLabel,
170 24
                'showField' => $showField,
171 24
                'showError' => $showError
172
            ]
173 24
        )->render();
174
    }
175
176
    /**
177
     * Get the attribute value from the model by name
178
     *
179
     * @param mixed $model
180
     * @param string $name
181
     * @return mixed
182
     */
183 55
    protected function getModelValueAttribute($model, $name)
184
    {
185 55
        $transformedName = $this->transformKey($name);
186 55
        if (is_string($model)) {
187
            return $model;
188 55
        } elseif (is_object($model)) {
189 2
            return object_get($model, $transformedName);
190 55
        } elseif (is_array($model)) {
191 54
            return array_get($model, $transformedName);
192
        }
193 5
    }
194
195
    /**
196
     * Transform array like syntax to dot syntax
197
     *
198
     * @param $key
199
     * @return mixed
200
     */
201 62
    protected function transformKey($key)
202
    {
203 62
        return $this->formHelper->transformToDotSyntax($key);
204
    }
205
206
    /**
207
     * Prepare options for rendering
208
     *
209
     * @param array $options
210
     * @return array
211
     */
212 62
    protected function prepareOptions(array $options = [])
213
    {
214 62
        $helper = $this->formHelper;
215 62
        $this->options = $helper->mergeOptions($this->options, $options);
216
217 62
        if ($this->getOption('attr.multiple') && !$this->getOption('tmp.multipleBracesSet')) {
218 2
            $this->name = $this->name.'[]';
219 2
            $this->setOption('tmp.multipleBracesSet', true);
220
        }
221
222 62
        if ($this->parent->haveErrorsEnabled()) {
223 62
            $this->addErrorClass();
224
        }
225
226 62
        if ($this->getOption('required') === true) {
227 1
            $lblClass = $this->getOption('label_attr.class', '');
228 1
            $requiredClass = $helper->getConfig('defaults.required_class', 'required');
229 1
            if (!str_contains($lblClass, $requiredClass)) {
230 1
                $lblClass .= ' ' . $requiredClass;
231 1
                $this->setOption('label_attr.class', $lblClass);
232 1
                $this->setOption('attr.required', 'required');
233
            }
234
        }
235
236 62
        if ($this->parent->clientValidationEnabled() && $rules = $this->getOption('rules')) {
237 4
            $rulesParser = new RulesParser($this);
238 4
            $attrs = $this->getOption('attr') + $rulesParser->parse($rules);
239 4
            $this->setOption('attr', $attrs);
240
        }
241
242 62
        $this->setOption('wrapperAttrs', $helper->prepareAttributes($this->getOption('wrapper')));
243 62
        $this->setOption('errorAttrs', $helper->prepareAttributes($this->getOption('errors')));
244
245 62
        if ($this->getOption('is_child')) {
246 16
            $this->setOption('labelAttrs', $helper->prepareAttributes($this->getOption('label_attr')));
247
        }
248
249 62
        if ($this->getOption('help_block.text')) {
250 1
            $this->setOption(
251 1
                'help_block.helpBlockAttrs',
252 1
                $helper->prepareAttributes($this->getOption('help_block.attr'))
253
            );
254
        }
255
256 62
        return $this->options;
257
    }
258
259
    /**
260
     * Get name of the field
261
     *
262
     * @return string
263
     */
264 26
    public function getName()
265
    {
266 26
        return $this->name;
267
    }
268
269
    /**
270
     * Set name of the field
271
     *
272
     * @param string $name
273
     * @return $this
274
     */
275 11
    public function setName($name)
276
    {
277 11
        $this->name = $name;
278
279 11
        return $this;
280
    }
281
282
    /**
283
     * Get dot notation key for fields
284
     *
285
     * @return string
286
     **/
287 35
    public function getNameKey()
288
    {
289 35
        return $this->transformKey($this->name);
290
    }
291
292
    /**
293
     * Get field options
294
     *
295
     * @return array
296
     */
297 9
    public function getOptions()
298
    {
299 9
        return $this->options;
300
    }
301
302
    /**
303
     * Get single option from options array. Can be used with dot notation ('attr.class')
304
     *
305
     * @param        $option
306
     * @param mixed  $default
307
     *
308
     * @return mixed
309
     */
310 62
    public function getOption($option, $default = null)
311
    {
312 62
        return array_get($this->options, $option, $default);
313
    }
314
315
    /**
316
     * Set field options
317
     *
318
     * @param array $options
319
     * @return $this
320
     */
321 11
    public function setOptions($options)
322
    {
323 11
        $this->options = $this->prepareOptions($options);
324
325 11
        return $this;
326
    }
327
328
    /**
329
     * Set single option on the field
330
     *
331
     * @param string $name
332
     * @param mixed $value
333
     * @return $this
334
     */
335 62
    public function setOption($name, $value)
336
    {
337 62
        array_set($this->options, $name, $value);
338
339 62
        return $this;
340
    }
341
342
    /**
343
     * Get the type of the field
344
     *
345
     * @return string
346
     */
347 34
    public function getType()
348
    {
349 34
        return $this->type;
350
    }
351
352
    /**
353
     * Set type of the field
354
     *
355
     * @param mixed $type
356
     * @return $this
357
     */
358 1
    public function setType($type)
359
    {
360 1
        if ($this->formHelper->getFieldType($type)) {
361 1
            $this->type = $type;
362
        }
363
364 1
        return $this;
365
    }
366
367
    /**
368
     * @return Form
369
     */
370 10
    public function getParent()
371
    {
372 10
        return $this->parent;
373
    }
374
375
    /**
376
     * Check if the field is rendered
377
     *
378
     * @return bool
379
     */
380 4
    public function isRendered()
381
    {
382 4
        return $this->rendered;
383
    }
384
385
    /**
386
     * Default options for field
387
     *
388
     * @return array
389
     */
390 44
    protected function getDefaults()
391
    {
392 44
        return [];
393
    }
394
395
    /**
396
     * Defaults used across all fields
397
     *
398
     * @return array
399
     */
400 62
    private function allDefaults()
401
    {
402
        return [
403 62
            'wrapper' => ['class' => $this->formHelper->getConfig('defaults.wrapper_class')],
404 62
            'attr' => ['class' => $this->formHelper->getConfig('defaults.field_class')],
405 62
            'help_block' => ['text' => null, 'tag' => 'p', 'attr' => [
406 62
                'class' => $this->formHelper->getConfig('defaults.help_block_class')
407
            ]],
408
            'value' => null,
409
            'default_value' => null,
410 62
            'label' => $this->formHelper->formatLabel($this->parent->getLocalizableName($this->getRealName())),
411
            'is_child' => false,
412 62
            'label_attr' => ['class' => $this->formHelper->getConfig('defaults.label_class')],
413 62
            'errors' => ['class' => $this->formHelper->getConfig('defaults.error_class')],
414
            'rules' => []
415
        ];
416
    }
417
418
    /**
419
     * Get real name of the field without form namespace
420
     *
421
     * @return string
422
     */
423 62
    public function getRealName()
424
    {
425 62
        return $this->getOption('real_name', $this->name);
426
    }
427
428
    /**
429
     * @param $value
430
     * @return $this
431
     */
432 56
    public function setValue($value)
433
    {
434 56
        if ($this->hasDefault) {
435 1
            return $this;
436
        }
437
438 56
        $closure = $this->valueClosure;
439
440 56
        if ($closure instanceof \Closure) {
441
            $value = $closure($value ?: null);
442
        }
443
444 56
        if (!$this->isValidValue($value)) {
445 54
            $value = $this->getOption($this->defaultValueProperty);
446
        }
447
448 56
        $this->options[$this->valueProperty] = $value;
449
450 56
        return $this;
451
    }
452
453
    /**
454
     * Set the template property on the object
455
     */
456 62
    private function setTemplate()
457
    {
458 62
        $this->template = $this->formHelper->getConfig($this->getTemplate(), $this->getTemplate());
459 62
    }
460
461
    /**
462
     * Add error class to wrapper if validation errors exist
463
     */
464 62
    protected function addErrorClass()
465
    {
466 62
        $errors = $this->parent->getRequest()->session()->get('errors');
467
468 62
        if ($errors && $errors->has($this->getNameKey())) {
469
            $errorClass = $this->formHelper->getConfig('defaults.wrapper_error_class');
470
            $wrapperClass = $this->getOption('wrapper.class');
471
472
            if ($this->getOption('wrapper') && !str_contains($wrapperClass, $errorClass)) {
473
                $wrapperClass .= ' ' . $errorClass;
474
                $this->setOption('wrapper.class', $wrapperClass);
475
            }
476
        }
477 62
    }
478
479
480
    /**
481
     * Merge all defaults with field specific defaults and set template if passed
482
     *
483
     * @param array $options
484
     */
485 62
    protected function setDefaultOptions(array $options = [])
486
    {
487 62
        $this->options = $this->formHelper->mergeOptions($this->allDefaults(), $this->getDefaults());
488 62
        $this->options = $this->prepareOptions($options);
489 62
    }
490
491
    /**
492
     * Check if fields needs label
493
     *
494
     * @return bool
495
     */
496 24
    protected function needsLabel()
497
    {
498
        // If field is <select> and child of choice, we don't need label for it
499 24
        $isChildSelect = $this->type == 'select' && $this->getOption('is_child') === true;
500
501 24
        if ($this->type == 'hidden' || $isChildSelect) {
502 6
            return false;
503
        }
504
505 23
        return true;
506
    }
507
508
    /**
509
     * Disable field
510
     *
511
     * @return $this
512
     */
513 1
    public function disable()
514
    {
515 1
        $this->setOption('attr.disabled', 'disabled');
516
517 1
        return $this;
518
    }
519
520
    /**
521
     * Enable field
522
     *
523
     * @return $this
524
     */
525 1
    public function enable()
526
    {
527 1
        array_forget($this->options, 'attr.disabled');
528
529 1
        return $this;
530
    }
531
532
    /**
533
     * Get validation rules for a field if any with label for attributes
534
     *
535
     * @return array|null
536
     */
537 3
    public function getValidationRules()
538
    {
539 3
        $rules = $this->getOption('rules', []);
540 3
        $name = $this->getNameKey();
541
542 3
        if (!$rules) {
543
            return null;
544
        }
545
546
        return [
547 3
            'rules' => [$name => $rules],
548 3
            'attributes' => [$name => $this->getOption('label')]
549
        ];
550
    }
551
552
    /**
553
     * Get value property
554
     *
555
     * @param mixed|null $default
556
     * @return mixed
557
     */
558 27
    public function getValue($default = null)
559
    {
560 27
        return $this->getOption($this->valueProperty, $default);
561
    }
562
563
    /**
564
     * Get default value property
565
     *
566
     * @param mixed|null $default
567
     * @return mixed
568
     */
569 24
    public function getDefaultValue($default = null)
570
    {
571 24
        return $this->getOption($this->defaultValueProperty, $default);
572
    }
573
574
    /**
575
     * Check if provided value is valid for this type
576
     *
577
     * @return bool
578
     */
579 59
    protected function isValidValue($value)
580
    {
581 59
        return $value !== null;
582
    }
583
}
584