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

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