Completed
Push — master ( b0b5e0...18cf69 )
by Kristijan
07:07
created

FormField::getModelValueAttribute()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.0219

Importance

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