Completed
Push — master ( 54b1af...d6de97 )
by Kristijan
11s
created

FormField::getOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
1
<?php
2
3
namespace Kris\LaravelFormBuilder\Fields;
4
5
use Kris\LaravelFormBuilder\Form;
6
use Illuminate\Database\Eloquent\Model;
7
use Kris\LaravelFormBuilder\FormHelper;
8
use Kris\LaravelFormBuilder\RulesParser;
9
use Illuminate\Database\Eloquent\Collection;
10
11
/**
12
 * Class FormField
13
 *
14
 * @package Kris\LaravelFormBuilder\Fields
15
 */
16
abstract class FormField
17
{
18
    /**
19
     * Name of the field
20
     *
21
     * @var
22
     */
23
    protected $name;
24
25
    /**
26
     * Type of the field
27
     *
28
     * @var
29
     */
30
    protected $type;
31
32
    /**
33
     * All options for the field
34
     *
35
     * @var
36
     */
37
    protected $options = [];
38
39
    /**
40
     * Is field rendered
41
     *
42
     * @var bool
43
     */
44
    protected $rendered = false;
45
46
    /**
47
     * @var Form
48
     */
49
    protected $parent;
50
51
    /**
52
     * @var string
53
     */
54
    protected $template;
55
56
    /**
57
     * @var FormHelper
58
     */
59
    protected $formHelper;
60
61
    /**
62
     * Name of the property for value setting
63
     *
64
     * @var string
65
     */
66
    protected $valueProperty = 'value';
67
68
    /**
69
     * Name of the property for default value
70
     *
71
     * @var string
72
     */
73
    protected $defaultValueProperty = 'default_value';
74
75
    /**
76
     * Is default value set?
77
     * @var bool
78
     */
79
    protected $hasDefault = false;
80
81
    /**
82
     * @var \Closure|null
83
     */
84
    protected $valueClosure = null;
85
86
    /**
87
     * @param             $name
88
     * @param             $type
89
     * @param Form        $parent
90
     * @param array       $options
91
     */
92 84
    public function __construct($name, $type, Form $parent, array $options = [])
93
    {
94 84
        $this->name = $name;
95 84
        $this->type = $type;
96 84
        $this->parent = $parent;
97 84
        $this->formHelper = $this->parent->getFormHelper();
98 84
        $this->setTemplate();
99 84
        $this->setDefaultOptions($options);
100 84
        $this->setupValue();
101 79
    }
102
103 84
    protected function setupValue()
104
    {
105 84
        $value = $this->getOption($this->valueProperty);
106 84
        $isChild = $this->getOption('is_child');
107
108 84
        if ($value instanceof \Closure) {
109
            $this->valueClosure = $value;
110
        }
111
112 84
        if (($value === null || $value instanceof \Closure) && !$isChild) {
113 75
            $this->setValue($this->getModelValueAttribute($this->parent->getModel(), $this->name));
114 20
        } elseif (!$isChild) {
115 12
            $this->hasDefault = true;
116
        }
117 79
    }
118
119
    /**
120
     * Get the template, can be config variable or view path
121
     *
122
     * @return string
123
     */
124
    abstract protected function getTemplate();
125
126
    /**
127
     * @return string
128
     */
129 32
    protected function getViewTemplate()
130
    {
131 32
        return $this->parent->getTemplatePrefix() . $this->getOption('template', $this->template);
132
    }
133
134
    /**
135
     * @param array $options
136
     * @param bool  $showLabel
137
     * @param bool  $showField
138
     * @param bool  $showError
139
     * @return string
140
     */
141 32
    public function render(array $options = [], $showLabel = true, $showField = true, $showError = true)
142
    {
143 32
        $this->prepareOptions($options);
144 32
        $value = $this->getValue();
145 32
        $defaultValue = $this->getDefaultValue();
146
147 32
        if ($showField) {
148 32
            $this->rendered = true;
149
        }
150
151
        // Override default value with value
152 32
        if (!$this->isValidValue($value) && $this->isValidValue($defaultValue)) {
153
            $this->setOption($this->valueProperty, $defaultValue);
154
        }
155
156 32
        if (!$this->needsLabel()) {
157 9
            $showLabel = false;
158
        }
159
160 32
        if ($showError) {
161 31
            $showError = $this->parent->haveErrorsEnabled();
162
        }
163
164 32
        $data = $this->getRenderData();
165
166 32
        return $this->formHelper->getView()->make(
167 32
            $this->getViewTemplate(),
168
            $data + [
169 32
                'name' => $this->name,
170 32
                'nameKey' => $this->getNameKey(),
171 32
                'type' => $this->type,
172 32
                'options' => $this->options,
173 32
                'showLabel' => $showLabel,
174 32
                'showField' => $showField,
175 32
                'showError' => $showError
176
            ]
177 32
        )->render();
178
    }
179
180
    /**
181
     * Return the extra render data for this form field, passed into the field's template directly.
182
     *
183
     * @return array
184
     */
185 32
    protected function getRenderData() {
186 32
        return [];
187
    }
188
189
    /**
190
     * Get the attribute value from the model by name
191
     *
192
     * @param mixed $model
193
     * @param string $name
194
     * @return mixed
195
     */
196 77
    protected function getModelValueAttribute($model, $name)
197
    {
198 77
        $transformedName = $this->transformKey($name);
199 77
        if (is_string($model)) {
200
            return $model;
201 77
        } elseif (is_object($model)) {
202 2
            return object_get($model, $transformedName);
203 77
        } elseif (is_array($model)) {
204 76
            return array_get($model, $transformedName);
205
        }
206 5
    }
207
208
    /**
209
     * Transform array like syntax to dot syntax
210
     *
211
     * @param $key
212
     * @return mixed
213
     */
214 84
    protected function transformKey($key)
215
    {
216 84
        return $this->formHelper->transformToDotSyntax($key);
217
    }
218
219
    /**
220
     * Prepare options for rendering
221
     *
222
     * @param array $options
223
     * @return array
224
     */
225 84
    protected function prepareOptions(array $options = [])
226
    {
227 84
        $helper = $this->formHelper;
228 84
        $rulesParser = new RulesParser($this);
229 84
        $rules = $this->getOption('rules');
230 84
        $parsedRules = $rules ? $rulesParser->parse($rules) : [];
231
232 84
        $this->options = $helper->mergeOptions($this->options, $options);
233
234 84
        foreach (['attr', 'label_attr', 'wrapper'] as $appendable) {
235
            // Append values to the 'class' attribute
236 84
            if ($this->getOption("{$appendable}.class_append")) {
237
                // Combine the current class attribute with the appends
238 3
                $append = $this->getOption("{$appendable}.class_append");
239 3
                $classAttribute = $this->getOption("{$appendable}.class", '').' '.$append;
240 3
                $this->setOption("{$appendable}.class", $classAttribute);
241
242
                // Then remove the class_append option to prevent it from showing up as an attribute in the HTML
243 84
                $this->setOption("{$appendable}.class_append", null);
244
            }
245
        }
246
247 84
        if ($this->getOption('attr.multiple') && !$this->getOption('tmp.multipleBracesSet')) {
248 2
            $this->name = $this->name.'[]';
249 2
            $this->setOption('tmp.multipleBracesSet', true);
250
        }
251
252 84
        if ($this->parent->haveErrorsEnabled()) {
253 84
            $this->addErrorClass();
254
        }
255
256 84
        if ($this->getOption('required') === true || isset($parsedRules['required'])) {
257 4
            $lblClass = $this->getOption('label_attr.class', '');
258 4
            $requiredClass = $helper->getConfig('defaults.required_class', 'required');
259
260 4
            if (! str_contains($lblClass, $requiredClass)) {
261 4
                $lblClass .= ' '.$requiredClass;
262 4
                $this->setOption('label_attr.class', $lblClass);
263
            }
264
265 4
            if ($this->parent->clientValidationEnabled()) {
266 3
                $this->setOption('attr.required', 'required');
267
268 3
                if ($parsedRules) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $parsedRules of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
269 1
                    $attrs = $this->getOption('attr') + $parsedRules;
270 1
                    $this->setOption('attr', $attrs);
271
                }
272
            }
273
        }
274
275 84
        $this->setOption('wrapperAttrs', $helper->prepareAttributes($this->getOption('wrapper')));
276 84
        $this->setOption('errorAttrs', $helper->prepareAttributes($this->getOption('errors')));
277
278 84
        if ($this->getOption('help_block.text')) {
279 1
            $this->setOption(
280 1
                'help_block.helpBlockAttrs',
281 1
                $helper->prepareAttributes($this->getOption('help_block.attr'))
282
            );
283
        }
284
285 84
        return $this->options;
286
    }
287
288
    /**
289
     * Get name of the field
290
     *
291
     * @return string
292
     */
293 27
    public function getName()
294
    {
295 27
        return $this->name;
296
    }
297
298
    /**
299
     * Set name of the field
300
     *
301
     * @param string $name
302
     * @return $this
303
     */
304 11
    public function setName($name)
305
    {
306 11
        $this->name = $name;
307
308 11
        return $this;
309
    }
310
311
    /**
312
     * Get dot notation key for fields
313
     *
314
     * @return string
315
     **/
316 50
    public function getNameKey()
317
    {
318 50
        return $this->transformKey($this->name);
319
    }
320
321
    /**
322
     * Get field options
323
     *
324
     * @return array
325
     */
326 12
    public function getOptions()
327
    {
328 12
        return $this->options;
329
    }
330
331
    /**
332
     * Get single option from options array. Can be used with dot notation ('attr.class')
333
     *
334
     * @param        $option
335
     * @param mixed  $default
336
     *
337
     * @return mixed
338
     */
339 84
    public function getOption($option, $default = null)
340
    {
341 84
        return array_get($this->options, $option, $default);
342
    }
343
344
    /**
345
     * Set field options
346
     *
347
     * @param array $options
348
     * @return $this
349
     */
350 11
    public function setOptions($options)
351
    {
352 11
        $this->options = $this->prepareOptions($options);
353
354 11
        return $this;
355
    }
356
357
    /**
358
     * Set single option on the field
359
     *
360
     * @param string $name
361
     * @param mixed $value
362
     * @return $this
363
     */
364 84
    public function setOption($name, $value)
365
    {
366 84
        array_set($this->options, $name, $value);
367
368 84
        return $this;
369
    }
370
371
    /**
372
     * Get the type of the field
373
     *
374
     * @return string
375
     */
376 51
    public function getType()
377
    {
378 51
        return $this->type;
379
    }
380
381
    /**
382
     * Set type of the field
383
     *
384
     * @param mixed $type
385
     * @return $this
386
     */
387 1
    public function setType($type)
388
    {
389 1
        if ($this->formHelper->getFieldType($type)) {
390 1
            $this->type = $type;
391
        }
392
393 1
        return $this;
394
    }
395
396
    /**
397
     * @return Form
398
     */
399 84
    public function getParent()
400
    {
401 84
        return $this->parent;
402
    }
403
404
    /**
405
     * Check if the field is rendered
406
     *
407
     * @return bool
408
     */
409 4
    public function isRendered()
410
    {
411 4
        return $this->rendered;
412
    }
413
414
    /**
415
     * Default options for field
416
     *
417
     * @return array
418
     */
419 64
    protected function getDefaults()
420
    {
421 64
        return [];
422
    }
423
424
    /**
425
     * Defaults used across all fields
426
     *
427
     * @return array
428
     */
429 84
    private function allDefaults()
430
    {
431
        return [
432 84
            'wrapper' => ['class' => $this->formHelper->getConfig('defaults.wrapper_class')],
433 84
            'attr' => ['class' => $this->formHelper->getConfig('defaults.field_class')],
434 84
            'help_block' => ['text' => null, 'tag' => 'p', 'attr' => [
435 84
                'class' => $this->formHelper->getConfig('defaults.help_block_class')
436
            ]],
437
            'value' => null,
438
            'default_value' => null,
439
            'label' => null,
440
            'label_show' => true,
441
            'is_child' => false,
442 84
            'label_attr' => ['class' => $this->formHelper->getConfig('defaults.label_class')],
443 84
            'errors' => ['class' => $this->formHelper->getConfig('defaults.error_class')],
444
            'rules' => [],
445
            'error_messages' => []
446
        ];
447
    }
448
449
    /**
450
     * Get real name of the field without form namespace
451
     *
452
     * @return string
453
     */
454 83
    public function getRealName()
455
    {
456 83
        return $this->getOption('real_name', $this->name);
457
    }
458
459
    /**
460
     * @param $value
461
     * @return $this
462
     */
463 78
    public function setValue($value)
464
    {
465 78
        if ($this->hasDefault) {
466 1
            return $this;
467
        }
468
469 78
        $closure = $this->valueClosure;
470
471 78
        if ($closure instanceof \Closure) {
472
            $value = $closure($value ?: null);
473
        }
474
475 78
        if (!$this->isValidValue($value)) {
476 76
            $value = $this->getOption($this->defaultValueProperty);
477
        }
478
479 78
        $this->options[$this->valueProperty] = $value;
480
481 78
        return $this;
482
    }
483
484
    /**
485
     * Set the template property on the object
486
     */
487 84
    private function setTemplate()
488
    {
489 84
        $this->template = $this->formHelper->getConfig($this->getTemplate(), $this->getTemplate());
490 84
    }
491
492
    /**
493
     * Add error class to wrapper if validation errors exist
494
     */
495 84
    protected function addErrorClass()
496
    {
497 84
        $errors = $this->parent->getRequest()->session()->get('errors');
498
499 84
        if ($errors && $errors->has($this->getNameKey())) {
500
            $errorClass = $this->formHelper->getConfig('defaults.wrapper_error_class');
501
            $wrapperClass = $this->getOption('wrapper.class');
502
503
            if ($this->getOption('wrapper') && !str_contains($wrapperClass, $errorClass)) {
504
                $wrapperClass .= ' ' . $errorClass;
505
                $this->setOption('wrapper.class', $wrapperClass);
506
            }
507
        }
508 84
    }
509
510
511
    /**
512
     * Merge all defaults with field specific defaults and set template if passed
513
     *
514
     * @param array $options
515
     */
516 84
    protected function setDefaultOptions(array $options = [])
517
    {
518 84
        $this->options = $this->formHelper->mergeOptions($this->allDefaults(), $this->getDefaults());
519 84
        $this->options = $this->prepareOptions($options);
520
521 84
        $defaults = $this->setDefaultClasses($options);
522 84
        $this->options = $this->formHelper->mergeOptions($this->options, $defaults);
523
524 84
        $this->setupLabel();
525 84
    }
526
527
    /**
528
     * Creates default wrapper classes for the form element.
529
     *
530
     * @param array $options
531
     * @return array
532
     */
533 84
    protected function setDefaultClasses(array $options = [])
534
    {
535 84
        $wrapper_class = $this->formHelper->getConfig('defaults.' . $this->type . '.wrapper_class', '');
536 84
        $label_class = $this->formHelper->getConfig('defaults.' . $this->type . '.label_class', '');
537 84
        $field_class = $this->formHelper->getConfig('defaults.' . $this->type . '.field_class', '');
538
539 84
        $defaults = [];
540 84
        if ($wrapper_class && !array_get($options, 'wrapper.class')) {
541
            $defaults['wrapper']['class'] = $wrapper_class;
542
        }
543 84
        if ($label_class && !array_get($options, 'label_attr.class')) {
544
            $defaults['label_attr']['class'] = $label_class;
545
        }
546 84
        if ($field_class && !array_get($options, 'attr.class')) {
547 1
            $defaults['attr']['class'] = $field_class;
548
        }
549 84
        return $defaults;
550
    }
551
552 84
    protected function setupLabel()
553
    {
554 84
        if ($this->getOption('label') !== null) {
555 22
            return;
556
        }
557
558 82
        if ($langName = $this->parent->getLanguageName()) {
559 4
            $label = sprintf('%s.%s', $langName, $this->getRealName());
560
        } else {
561 79
            $label = $this->getRealName();
562
        }
563
564 82
        $this->setOption('label', $this->formHelper->formatLabel($label));
565 82
    }
566
567
    /**
568
     * Check if fields needs label
569
     *
570
     * @return bool
571
     */
572 32
    protected function needsLabel()
573
    {
574
        // If field is <select> and child of choice, we don't need label for it
575 32
        $isChildSelect = $this->type == 'select' && $this->getOption('is_child') === true;
576
577 32
        if ($this->type == 'hidden' || $isChildSelect) {
578 9
            return false;
579
        }
580
581 28
        return true;
582
    }
583
584
    /**
585
     * Disable field
586
     *
587
     * @return $this
588
     */
589 1
    public function disable()
590
    {
591 1
        $this->setOption('attr.disabled', 'disabled');
592
593 1
        return $this;
594
    }
595
596
    /**
597
     * Enable field
598
     *
599
     * @return $this
600
     */
601 1
    public function enable()
602
    {
603 1
        array_forget($this->options, 'attr.disabled');
604
605 1
        return $this;
606
    }
607
608
    /**
609
     * Get validation rules for a field if any with label for attributes
610
     *
611
     * @return array|null
612
     */
613 8
    public function getValidationRules()
614
    {
615 8
        $rules = $this->getOption('rules', []);
616 8
        $name = $this->getNameKey();
617 8
        $messages = $this->getOption('error_messages', []);
618 8
        $formName = $this->formHelper->transformToDotSyntax($this->parent->getName());
619
620 8
        if ($messages && $formName) {
621 1
            $newMessages = [];
622 1
            foreach ($messages as $messageKey => $message) {
623 1
                $messageKey = sprintf('%s.%s', $formName, $messageKey);
624 1
                $newMessages[$messageKey] = $message;
625
            }
626 1
            $messages = $newMessages;
627
        }
628
629 8
        if (!$rules) {
630 2
            return [];
631
        }
632
633
        return [
634 7
            'rules' => [$name => $rules],
635 7
            'attributes' => [$name => $this->getOption('label')],
636 7
            'error_messages' => $messages
637
        ];
638
    }
639
640
    /**
641
     * Get this field's attributes, probably just one.
642
     *
643
     * @return array
644
     */
645 3
    public function getAllAttributes()
646
    {
647 3
        return [$this->getNameKey()];
648
    }
649
650
    /**
651
     * Get value property
652
     *
653
     * @param mixed|null $default
654
     * @return mixed
655
     */
656 35
    public function getValue($default = null)
657
    {
658 35
        return $this->getOption($this->valueProperty, $default);
659
    }
660
661
    /**
662
     * Get default value property
663
     *
664
     * @param mixed|null $default
665
     * @return mixed
666
     */
667 32
    public function getDefaultValue($default = null)
668
    {
669 32
        return $this->getOption($this->defaultValueProperty, $default);
670
    }
671
672
    /**
673
     * Check if provided value is valid for this type
674
     *
675
     * @return bool
676
     */
677 79
    protected function isValidValue($value)
678
    {
679 79
        return $value !== null;
680
    }
681
}
682