Completed
Push — master ( 6ed074...69a7c6 )
by Song
02:23
created

Field::disable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Form;
4
5
use Closure;
6
use Encore\Admin\Admin;
7
use Encore\Admin\Form;
8
use Illuminate\Contracts\Support\Arrayable;
9
use Illuminate\Contracts\Support\Renderable;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Str;
12
use Illuminate\Support\Traits\Macroable;
13
14
/**
15
 * Class Field.
16
 */
17
class Field implements Renderable
18
{
19
    use Macroable;
20
21
    const FILE_DELETE_FLAG = '_file_del_';
22
    const FILE_SORT_FLAG = '_file_sort_';
23
24
    /**
25
     * Element id.
26
     *
27
     * @var array|string
28
     */
29
    protected $id;
30
31
    /**
32
     * Element value.
33
     *
34
     * @var mixed
35
     */
36
    protected $value;
37
38
    /**
39
     * Data of all original columns of value.
40
     *
41
     * @var mixed
42
     */
43
    protected $data;
44
45
    /**
46
     * Field original value.
47
     *
48
     * @var mixed
49
     */
50
    protected $original;
51
52
    /**
53
     * Field default value.
54
     *
55
     * @var mixed
56
     */
57
    protected $default;
58
59
    /**
60
     * Element label.
61
     *
62
     * @var string
63
     */
64
    protected $label = '';
65
66
    /**
67
     * Column name.
68
     *
69
     * @var string|array
70
     */
71
    protected $column = '';
72
73
    /**
74
     * Form element name.
75
     *
76
     * @var string
77
     */
78
    protected $elementName = [];
79
80
    /**
81
     * Form element classes.
82
     *
83
     * @var array
84
     */
85
    protected $elementClass = [];
86
87
    /**
88
     * Variables of elements.
89
     *
90
     * @var array
91
     */
92
    protected $variables = [];
93
94
    /**
95
     * Options for specify elements.
96
     *
97
     * @var array
98
     */
99
    protected $options = [];
100
101
    /**
102
     * Checked for specify elements.
103
     *
104
     * @var array
105
     */
106
    protected $checked = [];
107
108
    /**
109
     * Validation rules.
110
     *
111
     * @var array|\Closure
112
     */
113
    protected $rules = [];
114
115
    /**
116
     * The validation rules for creation.
117
     *
118
     * @var array|\Closure
119
     */
120
    public $creationRules = [];
121
122
    /**
123
     * The validation rules for updates.
124
     *
125
     * @var array|\Closure
126
     */
127
    public $updateRules = [];
128
129
    /**
130
     * @var \Closure
131
     */
132
    protected $validator;
133
134
    /**
135
     * Validation messages.
136
     *
137
     * @var array
138
     */
139
    protected $validationMessages = [];
140
141
    /**
142
     * Css required by this field.
143
     *
144
     * @var array
145
     */
146
    protected static $css = [];
147
148
    /**
149
     * Js required by this field.
150
     *
151
     * @var array
152
     */
153
    protected static $js = [];
154
155
    /**
156
     * Script for field.
157
     *
158
     * @var string
159
     */
160
    protected $script = '';
161
162
    /**
163
     * Element attributes.
164
     *
165
     * @var array
166
     */
167
    protected $attributes = [];
168
169
    /**
170
     * Parent form.
171
     *
172
     * @var Form
173
     */
174
    protected $form = null;
175
176
    /**
177
     * View for field to render.
178
     *
179
     * @var string
180
     */
181
    protected $view = '';
182
183
    /**
184
     * Help block.
185
     *
186
     * @var array
187
     */
188
    protected $help = [];
189
190
    /**
191
     * Key for errors.
192
     *
193
     * @var mixed
194
     */
195
    protected $errorKey;
196
197
    /**
198
     * Placeholder for this field.
199
     *
200
     * @var string|array
201
     */
202
    protected $placeholder;
203
204
    /**
205
     * Width for label and field.
206
     *
207
     * @var array
208
     */
209
    protected $width = [
210
        'label' => 2,
211
        'field' => 8,
212
    ];
213
214
    /**
215
     * If the form horizontal layout.
216
     *
217
     * @var bool
218
     */
219
    protected $horizontal = true;
220
221
    /**
222
     * column data format.
223
     *
224
     * @var \Closure
225
     */
226
    protected $customFormat = null;
227
228
    /**
229
     * @var bool
230
     */
231
    protected $display = true;
232
233
    /**
234
     * @var array
235
     */
236
    protected $labelClass = [];
237
238
    /**
239
     * @var array
240
     */
241
    protected $groupClass = [];
242
243
    /**
244
     * @var \Closure
245
     */
246
    protected $callback;
247
248
    /**
249
     * @var bool
250
     */
251
    public $isJsonType = false;
252
253
    /**
254
     * Field constructor.
255
     *
256
     * @param       $column
257
     * @param array $arguments
258
     */
259
    public function __construct($column = '', $arguments = [])
260
    {
261
        $this->column = $this->formatColumn($column);
262
        $this->label = $this->formatLabel($arguments);
263
        $this->id = $this->formatId($column);
264
    }
265
266
    /**
267
     * Get assets required by this field.
268
     *
269
     * @return array
270
     */
271
    public static function getAssets()
272
    {
273
        return [
274
            'css' => static::$css,
275
            'js'  => static::$js,
276
        ];
277
    }
278
279
    /**
280
     * Format the field column name.
281
     *
282
     * @param string $column
283
     *
284
     * @return mixed|string
285
     */
286
    protected function formatColumn($column = '')
287
    {
288 View Code Duplication
        if (Str::contains($column, '->')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
289
            $this->isJsonType = true;
290
291
            $column = str_replace('->', '.', $column);
292
        }
293
294
        return $column;
295
    }
296
297
    /**
298
     * Format the field element id.
299
     *
300
     * @param string|array $column
301
     *
302
     * @return string|array
303
     */
304
    protected function formatId($column)
305
    {
306
        return str_replace('.', '_', $column);
307
    }
308
309
    /**
310
     * Format the label value.
311
     *
312
     * @param array $arguments
313
     *
314
     * @return string
315
     */
316
    protected function formatLabel($arguments = [])
317
    {
318
        $column = is_array($this->column) ? current($this->column) : $this->column;
319
320
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($column);
321
322
        return str_replace(['.', '_', '->'], ' ', $label);
323
    }
324
325
    /**
326
     * Format the name of the field.
327
     *
328
     * @param string $column
329
     *
330
     * @return array|mixed|string
331
     */
332
    protected function formatName($column)
333
    {
334
        if (is_string($column)) {
335 View Code Duplication
            if (Str::contains($column, '->')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
336
                $name = explode('->', $column);
337
            } else {
338
                $name = explode('.', $column);
339
            }
340
341
            if (count($name) == 1) {
342
                return $name[0];
343
            }
344
345
            $html = array_shift($name);
346
            foreach ($name as $piece) {
347
                $html .= "[$piece]";
348
            }
349
350
            return $html;
351
        }
352
353
        if (is_array($this->column)) {
354
            $names = [];
355
            foreach ($this->column as $key => $name) {
356
                $names[$key] = $this->formatName($name);
357
            }
358
359
            return $names;
360
        }
361
362
        return '';
363
    }
364
365
    /**
366
     * Set form element name.
367
     *
368
     * @param string $name
369
     *
370
     * @return $this
371
     *
372
     * @author Edwin Hui
373
     */
374
    public function setElementName($name)
375
    {
376
        $this->elementName = $name;
377
378
        return $this;
379
    }
380
381
    /**
382
     * Fill data to the field.
383
     *
384
     * @param array $data
385
     *
386
     * @return void
387
     */
388
    public function fill($data)
389
    {
390
        $this->data = $data;
391
392
        if (is_array($this->column)) {
393
            foreach ($this->column as $key => $column) {
394
                $this->value[$key] = Arr::get($data, $column);
395
            }
396
397
            return;
398
        }
399
400
        $this->value = Arr::get($data, $this->column);
401
        if (isset($this->customFormat) && $this->customFormat instanceof \Closure) {
402
            $this->value = call_user_func($this->customFormat, $this->value);
403
        }
404
    }
405
406
    /**
407
     * custom format form column data when edit.
408
     *
409
     * @param \Closure $call
410
     *
411
     * @return $this
412
     */
413
    public function customFormat(\Closure $call)
414
    {
415
        $this->customFormat = $call;
416
417
        return $this;
418
    }
419
420
    /**
421
     * Set original value to the field.
422
     *
423
     * @param array $data
424
     *
425
     * @return void
426
     */
427
    public function setOriginal($data)
428
    {
429
        if (is_array($this->column)) {
430
            foreach ($this->column as $key => $column) {
431
                $this->original[$key] = Arr::get($data, $column);
432
            }
433
434
            return;
435
        }
436
437
        $this->original = Arr::get($data, $this->column);
438
    }
439
440
    /**
441
     * @param Form $form
442
     *
443
     * @return $this
444
     */
445
    public function setForm(Form $form = null)
446
    {
447
        $this->form = $form;
448
449
        return $this;
450
    }
451
452
    /**
453
     * Set width for field and label.
454
     *
455
     * @param int $field
456
     * @param int $label
457
     *
458
     * @return $this
459
     */
460
    public function setWidth($field = 8, $label = 2)
461
    {
462
        $this->width = [
463
            'label' => $label,
464
            'field' => $field,
465
        ];
466
467
        return $this;
468
    }
469
470
    /**
471
     * Set the field options.
472
     *
473
     * @param array $options
474
     *
475
     * @return $this
476
     */
477 View Code Duplication
    public function options($options = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
    {
479
        if ($options instanceof Arrayable) {
480
            $options = $options->toArray();
481
        }
482
483
        $this->options = array_merge($this->options, $options);
484
485
        return $this;
486
    }
487
488
    /**
489
     * Set the field option checked.
490
     *
491
     * @param array $checked
492
     *
493
     * @return $this
494
     */
495 View Code Duplication
    public function checked($checked = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
496
    {
497
        if ($checked instanceof Arrayable) {
498
            $checked = $checked->toArray();
499
        }
500
501
        $this->checked = array_merge($this->checked, $checked);
502
503
        return $this;
504
    }
505
506
    /**
507
     * Add `required` attribute to current field if has required rule,
508
     * except file and image fields.
509
     *
510
     * @param array $rules
511
     */
512
    protected function addRequiredAttribute($rules)
513
    {
514
        if (!is_array($rules)) {
515
            return;
516
        }
517
518
        if (!in_array('required', $rules)) {
519
            return;
520
        }
521
522
        if ($this instanceof Form\Field\MultipleFile
523
            || $this instanceof Form\Field\File) {
524
            return;
525
        }
526
527
        $this->required();
528
    }
529
530
    /**
531
     * If has `required` rule, add required attribute to this field.
532
     */
533
    protected function addRequiredAttributeFromRules()
534
    {
535
        if (is_null($this->data)) {
536
            // Create page
537
            $rules = $this->creationRules ?: $this->rules;
538
        } else {
539
            // Update page
540
            $rules = $this->updateRules ?: $this->rules;
541
        }
542
543
        $this->addRequiredAttribute($rules);
0 ignored issues
show
Bug introduced by
It seems like $rules can also be of type object<Closure>; however, Encore\Admin\Form\Field::addRequiredAttribute() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
544
    }
545
546
    /**
547
     * Format validation rules.
548
     *
549
     * @param array|string $rules
550
     *
551
     * @return array
552
     */
553
    protected function formatRules($rules)
554
    {
555
        if (is_string($rules)) {
556
            $rules = array_filter(explode('|', $rules));
557
        }
558
559
        return array_filter((array) $rules);
560
    }
561
562
    /**
563
     * @param string|array|Closure $input
564
     * @param string|array         $original
565
     *
566
     * @return array|Closure
567
     */
568
    protected function mergeRules($input, $original)
569
    {
570
        if ($input instanceof Closure) {
571
            $rules = $input;
572
        } else {
573
            if (!empty($original)) {
574
                $original = $this->formatRules($original);
575
            }
576
577
            $rules = array_merge($original, $this->formatRules($input));
578
        }
579
580
        return $rules;
581
    }
582
583
    /**
584
     * Set the validation rules for the field.
585
     *
586
     * @param array|callable|string $rules
587
     * @param array                 $messages
588
     *
589
     * @return $this
590
     */
591 View Code Duplication
    public function rules($rules = null, $messages = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
592
    {
593
        $this->rules = $this->mergeRules($rules, $this->rules);
0 ignored issues
show
Documentation introduced by
$rules is of type callable|null, but the function expects a string|array|object<Closure>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $this->rules can also be of type object<Closure>; however, Encore\Admin\Form\Field::mergeRules() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
594
595
        $this->setValidationMessages('default', $messages);
596
597
        return $this;
598
    }
599
600
    /**
601
     * Set the update validation rules for the field.
602
     *
603
     * @param array|callable|string $rules
604
     * @param array                 $messages
605
     *
606
     * @return $this
607
     */
608 View Code Duplication
    public function updateRules($rules = null, $messages = [])
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
609
    {
610
        $this->updateRules = $this->mergeRules($rules, $this->updateRules);
0 ignored issues
show
Documentation introduced by
$rules is of type callable|null, but the function expects a string|array|object<Closure>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $this->updateRules can also be of type object<Closure>; however, Encore\Admin\Form\Field::mergeRules() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
611
612
        $this->setValidationMessages('update', $messages);
613
614
        return $this;
615
    }
616
617
    /**
618
     * Set the creation validation rules for the field.
619
     *
620
     * @param array|callable|string $rules
621
     * @param array                 $messages
622
     *
623
     * @return $this
624
     */
625
    public function creationRules($rules = null, $messages = [])
626
    {
627
        $this->creationRules = $this->mergeRules($rules, $this->creationRules);
0 ignored issues
show
Documentation introduced by
$rules is of type callable|null, but the function expects a string|array|object<Closure>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $this->creationRules can also be of type object<Closure>; however, Encore\Admin\Form\Field::mergeRules() does only seem to accept string|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
628
629
        $this->setValidationMessages('creation', $messages);
630
631
        return $this;
632
    }
633
634
    /**
635
     * Set validation messages for column.
636
     *
637
     * @param string $key
638
     * @param array  $messages
639
     *
640
     * @return $this
641
     */
642
    public function setValidationMessages($key, array $messages)
643
    {
644
        $this->validationMessages[$key] = $messages;
645
646
        return $this;
647
    }
648
649
    /**
650
     * Get validation messages for the field.
651
     *
652
     * @return array|mixed
653
     */
654
    public function getValidationMessages()
655
    {
656
        // Default validation message.
657
        $messages = $this->validationMessages['default'] ?? [];
658
659
        if (request()->isMethod('POST')) {
660
            $messages = $this->validationMessages['creation'] ?? $messages;
661
        } elseif (request()->isMethod('PUT')) {
662
            $messages = $this->validationMessages['update'] ?? $messages;
663
        }
664
665
        return $messages;
666
    }
667
668
    /**
669
     * Get field validation rules.
670
     *
671
     * @return string
672
     */
673
    protected function getRules()
674
    {
675
        if (request()->isMethod('POST')) {
676
            $rules = $this->creationRules ?: $this->rules;
677
        } elseif (request()->isMethod('PUT')) {
678
            $rules = $this->updateRules ?: $this->rules;
679
        } else {
680
            $rules = $this->rules;
681
        }
682
683
        if ($rules instanceof \Closure) {
684
            $rules = $rules->call($this, $this->form);
685
        }
686
687
        if (is_string($rules)) {
688
            $rules = array_filter(explode('|', $rules));
689
        }
690
691
        if (!$this->form) {
692
            return $rules;
693
        }
694
695
        if (!$id = $this->form->model()->getKey()) {
696
            return $rules;
697
        }
698
699
        foreach ($rules as &$rule) {
0 ignored issues
show
Bug introduced by
The expression $rules of type object|integer|double|null|array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
700
            if (is_string($rule)) {
701
                $rule = str_replace('{{id}}', $id, $rule);
702
            }
703
        }
704
705
        return $rules;
706
    }
707
708
    /**
709
     * Remove a specific rule by keyword.
710
     *
711
     * @param string $rule
712
     *
713
     * @return void
714
     */
715
    protected function removeRule($rule)
716
    {
717
        if (!is_string($this->rules)) {
718
            return;
719
        }
720
721
        $pattern = "/{$rule}[^\|]?(\||$)/";
722
        $this->rules = preg_replace($pattern, '', $this->rules, -1);
0 ignored issues
show
Documentation Bug introduced by
It seems like preg_replace($pattern, '', $this->rules, -1) of type string is incompatible with the declared type array|object<Closure> of property $rules.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
723
    }
724
725
    /**
726
     * Set field validator.
727
     *
728
     * @param callable $validator
729
     *
730
     * @return $this
731
     */
732
    public function validator(callable $validator)
733
    {
734
        $this->validator = $validator;
0 ignored issues
show
Documentation Bug introduced by
It seems like $validator of type callable is incompatible with the declared type object<Closure> of property $validator.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
735
736
        return $this;
737
    }
738
739
    /**
740
     * Get key for error message.
741
     *
742
     * @return string
743
     */
744
    public function getErrorKey()
745
    {
746
        return $this->errorKey ?: $this->column;
747
    }
748
749
    /**
750
     * Set key for error message.
751
     *
752
     * @param string $key
753
     *
754
     * @return $this
755
     */
756
    public function setErrorKey($key)
757
    {
758
        $this->errorKey = $key;
759
760
        return $this;
761
    }
762
763
    /**
764
     * Set or get value of the field.
765
     *
766
     * @param null $value
767
     *
768
     * @return mixed
769
     */
770 View Code Duplication
    public function value($value = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
771
    {
772
        if (is_null($value)) {
773
            return is_null($this->value) ? $this->getDefault() : $this->value;
774
        }
775
776
        $this->value = $value;
777
778
        return $this;
779
    }
780
781
    /**
782
     * Set or get data.
783
     *
784
     * @param array $data
785
     *
786
     * @return $this
787
     */
788
    public function data(array $data = null)
789
    {
790
        if (is_null($data)) {
791
            return $this->data;
792
        }
793
794
        $this->data = $data;
795
796
        return $this;
797
    }
798
799
    /**
800
     * Set default value for field.
801
     *
802
     * @param $default
803
     *
804
     * @return $this
805
     */
806
    public function default($default)
807
    {
808
        $this->default = $default;
809
810
        return $this;
811
    }
812
813
    /**
814
     * Get default value.
815
     *
816
     * @return mixed
817
     */
818
    public function getDefault()
819
    {
820
        if ($this->default instanceof \Closure) {
821
            return call_user_func($this->default, $this->form);
822
        }
823
824
        return $this->default;
825
    }
826
827
    /**
828
     * Set help block for current field.
829
     *
830
     * @param string $text
831
     * @param string $icon
832
     *
833
     * @return $this
834
     */
835
    public function help($text = '', $icon = 'fa-info-circle')
836
    {
837
        $this->help = compact('text', 'icon');
838
839
        return $this;
840
    }
841
842
    /**
843
     * Get column of the field.
844
     *
845
     * @return string|array
846
     */
847
    public function column()
848
    {
849
        return $this->column;
850
    }
851
852
    /**
853
     * Get label of the field.
854
     *
855
     * @return string
856
     */
857
    public function label()
858
    {
859
        return $this->label;
860
    }
861
862
    /**
863
     * Get original value of the field.
864
     *
865
     * @return mixed
866
     */
867
    public function original()
868
    {
869
        return $this->original;
870
    }
871
872
    /**
873
     * Get validator for this field.
874
     *
875
     * @param array $input
876
     *
877
     * @return bool|\Illuminate\Contracts\Validation\Validator|mixed
878
     */
879
    public function getValidator(array $input)
880
    {
881
        if ($this->validator) {
882
            return $this->validator->call($this, $input);
883
        }
884
885
        $rules = $attributes = [];
886
887
        if (!$fieldRules = $this->getRules()) {
888
            return false;
889
        }
890
891
        if (is_string($this->column)) {
892
            if (!Arr::has($input, $this->column)) {
893
                return false;
894
            }
895
896
            $input = $this->sanitizeInput($input, $this->column);
897
898
            $rules[$this->column] = $fieldRules;
899
            $attributes[$this->column] = $this->label;
900
        }
901
902
        if (is_array($this->column)) {
903
            foreach ($this->column as $key => $column) {
904
                if (!array_key_exists($column, $input)) {
905
                    continue;
906
                }
907
                $input[$column.$key] = Arr::get($input, $column);
908
                $rules[$column.$key] = $fieldRules;
909
                $attributes[$column.$key] = $this->label."[$column]";
910
            }
911
        }
912
913
        return \validator($input, $rules, $this->getValidationMessages(), $attributes);
914
    }
915
916
    /**
917
     * Sanitize input data.
918
     *
919
     * @param array  $input
920
     * @param string $column
921
     *
922
     * @return array
923
     */
924
    protected function sanitizeInput($input, $column)
925
    {
926
        if ($this instanceof Field\MultipleSelect) {
927
            $value = Arr::get($input, $column);
928
            Arr::set($input, $column, array_filter($value));
929
        }
930
931
        return $input;
932
    }
933
934
    /**
935
     * Add html attributes to elements.
936
     *
937
     * @param array|string $attribute
938
     * @param mixed        $value
939
     *
940
     * @return $this
941
     */
942
    public function attribute($attribute, $value = null)
943
    {
944
        if (is_array($attribute)) {
945
            $this->attributes = array_merge($this->attributes, $attribute);
946
        } else {
947
            $this->attributes[$attribute] = (string) $value;
948
        }
949
950
        return $this;
951
    }
952
953
    /**
954
     * Specifies a regular expression against which to validate the value of the input.
955
     *
956
     * @param string $regexp
957
     *
958
     * @return Field
959
     */
960
    public function pattern($regexp)
961
    {
962
        return $this->attribute('pattern', $regexp);
963
    }
964
965
    /**
966
     * set the input filed required.
967
     *
968
     * @param bool $isLabelAsterisked
969
     *
970
     * @return Field
971
     */
972
    public function required($isLabelAsterisked = true)
973
    {
974
        if ($isLabelAsterisked) {
975
            $this->setLabelClass(['asterisk']);
976
        }
977
978
        return $this->attribute('required', true);
979
    }
980
981
    /**
982
     * Set the field automatically get focus.
983
     *
984
     * @return Field
985
     */
986
    public function autofocus()
987
    {
988
        return $this->attribute('autofocus', true);
989
    }
990
991
    /**
992
     * Set the field as readonly mode.
993
     *
994
     * @return Field
995
     */
996
    public function readonly()
997
    {
998
        return $this->attribute('readonly', true);
999
    }
1000
1001
    /**
1002
     * Set field as disabled.
1003
     *
1004
     * @return Field
1005
     */
1006
    public function disable()
1007
    {
1008
        return $this->attribute('disabled', true);
1009
    }
1010
1011
    /**
1012
     * Set field placeholder.
1013
     *
1014
     * @param string $placeholder
1015
     *
1016
     * @return Field
1017
     */
1018
    public function placeholder($placeholder = '')
1019
    {
1020
        $this->placeholder = $placeholder;
1021
1022
        return $this;
1023
    }
1024
1025
    /**
1026
     * Get placeholder.
1027
     *
1028
     * @return string
1029
     */
1030
    public function getPlaceholder()
1031
    {
1032
        return $this->placeholder ?: trans('admin.input').' '.$this->label;
1033
    }
1034
1035
    /**
1036
     * Prepare for a field value before update or insert.
1037
     *
1038
     * @param $value
1039
     *
1040
     * @return mixed
1041
     */
1042
    public function prepare($value)
1043
    {
1044
        return $value;
1045
    }
1046
1047
    /**
1048
     * Format the field attributes.
1049
     *
1050
     * @return string
1051
     */
1052
    protected function formatAttributes()
1053
    {
1054
        $html = [];
1055
1056
        foreach ($this->attributes as $name => $value) {
1057
            $html[] = $name.'="'.e($value).'"';
1058
        }
1059
1060
        return implode(' ', $html);
1061
    }
1062
1063
    /**
1064
     * @return $this
1065
     */
1066
    public function disableHorizontal()
1067
    {
1068
        $this->horizontal = false;
1069
1070
        return $this;
1071
    }
1072
1073
    /**
1074
     * @return array
1075
     */
1076
    public function getViewElementClasses()
1077
    {
1078
        if ($this->horizontal) {
1079
            return [
1080
                'label'      => "col-sm-{$this->width['label']} {$this->getLabelClass()}",
1081
                'field'      => "col-sm-{$this->width['field']}",
1082
                'form-group' => $this->getGroupClass(true),
1083
            ];
1084
        }
1085
1086
        return ['label' => "{$this->getLabelClass()}", 'field' => '', 'form-group' => ''];
1087
    }
1088
1089
    /**
1090
     * Set form element class.
1091
     *
1092
     * @param string|array $class
1093
     *
1094
     * @return $this
1095
     */
1096
    public function setElementClass($class)
1097
    {
1098
        $this->elementClass = array_merge($this->elementClass, (array) $class);
1099
1100
        return $this;
1101
    }
1102
1103
    /**
1104
     * Get element class.
1105
     *
1106
     * @return array
1107
     */
1108
    protected function getElementClass()
1109
    {
1110
        if (!$this->elementClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->elementClass 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...
1111
            $name = $this->elementName ?: $this->formatName($this->column);
0 ignored issues
show
Bug introduced by
It seems like $this->column can also be of type array; however, Encore\Admin\Form\Field::formatName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1112
1113
            $this->elementClass = (array) str_replace(['[', ']'], '_', $name);
1114
        }
1115
1116
        return $this->elementClass;
1117
    }
1118
1119
    /**
1120
     * Get element class string.
1121
     *
1122
     * @return mixed
1123
     */
1124 View Code Duplication
    protected function getElementClassString()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1125
    {
1126
        $elementClass = $this->getElementClass();
1127
1128
        if (Arr::isAssoc($elementClass)) {
1129
            $classes = [];
1130
1131
            foreach ($elementClass as $index => $class) {
1132
                $classes[$index] = is_array($class) ? implode(' ', $class) : $class;
1133
            }
1134
1135
            return $classes;
1136
        }
1137
1138
        return implode(' ', $elementClass);
1139
    }
1140
1141
    /**
1142
     * Get element class selector.
1143
     *
1144
     * @return string|array
1145
     */
1146 View Code Duplication
    protected function getElementClassSelector()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1147
    {
1148
        $elementClass = $this->getElementClass();
1149
1150
        if (Arr::isAssoc($elementClass)) {
1151
            $classes = [];
1152
1153
            foreach ($elementClass as $index => $class) {
1154
                $classes[$index] = '.'.(is_array($class) ? implode('.', $class) : $class);
1155
            }
1156
1157
            return $classes;
1158
        }
1159
1160
        return '.'.implode('.', $elementClass);
1161
    }
1162
1163
    /**
1164
     * Add the element class.
1165
     *
1166
     * @param $class
1167
     *
1168
     * @return $this
1169
     */
1170
    public function addElementClass($class)
1171
    {
1172
        if (is_array($class) || is_string($class)) {
1173
            $this->elementClass = array_merge($this->elementClass, (array) $class);
1174
1175
            $this->elementClass = array_unique($this->elementClass);
1176
        }
1177
1178
        return $this;
1179
    }
1180
1181
    /**
1182
     * Remove element class.
1183
     *
1184
     * @param $class
1185
     *
1186
     * @return $this
1187
     */
1188
    public function removeElementClass($class)
1189
    {
1190
        $delClass = [];
1191
1192
        if (is_string($class) || is_array($class)) {
1193
            $delClass = (array) $class;
1194
        }
1195
1196
        foreach ($delClass as $del) {
1197
            if (($key = array_search($del, $this->elementClass)) !== false) {
1198
                unset($this->elementClass[$key]);
1199
            }
1200
        }
1201
1202
        return $this;
1203
    }
1204
1205
    /**
1206
     * Set form group class.
1207
     *
1208
     * @param string|array $class
1209
     *
1210
     * @return $this
1211
     */
1212
    public function setGroupClass($class)
1213
    : self
1214
    {
1215
        if (is_array($class)) {
1216
            $this->groupClass = array_merge($this->groupClass, $class);
1217
        } else {
1218
            array_push($this->groupClass, $class);
1219
        }
1220
1221
        return $this;
1222
    }
1223
1224
    /**
1225
     * Get element class.
1226
     *
1227
     * @param bool $default
1228
     *
1229
     * @return string
1230
     */
1231
    protected function getGroupClass($default = false)
1232
    : string
1233
    {
1234
        return ($default ? 'form-group ' : '').implode(' ', array_filter($this->groupClass));
1235
    }
1236
1237
    /**
1238
     * reset field className.
1239
     *
1240
     * @param string $className
1241
     * @param string $resetClassName
1242
     *
1243
     * @return $this
1244
     */
1245
    public function resetElementClassName(string $className, string $resetClassName)
1246
    {
1247
        if (($key = array_search($className, $this->getElementClass())) !== false) {
1248
            $this->elementClass[$key] = $resetClassName;
1249
        }
1250
1251
        return $this;
1252
    }
1253
1254
    /**
1255
     * Add variables to field view.
1256
     *
1257
     * @param array $variables
1258
     *
1259
     * @return $this
1260
     */
1261
    protected function addVariables(array $variables = [])
1262
    {
1263
        $this->variables = array_merge($this->variables, $variables);
1264
1265
        return $this;
1266
    }
1267
1268
    /**
1269
     * @return string
1270
     */
1271
    public function getLabelClass()
1272
    : string
1273
    {
1274
        return implode(' ', $this->labelClass);
1275
    }
1276
1277
    /**
1278
     * @param array $labelClass
1279
     *
1280
     * @return self
1281
     */
1282
    public function setLabelClass(array $labelClass)
1283
    : self
1284
    {
1285
        $this->labelClass = $labelClass;
1286
1287
        return $this;
1288
    }
1289
1290
    /**
1291
     * Get the view variables of this field.
1292
     *
1293
     * @return array
1294
     */
1295
    public function variables()
1296
    {
1297
        return array_merge($this->variables, [
1298
            'id'          => $this->id,
1299
            'name'        => $this->elementName ?: $this->formatName($this->column),
0 ignored issues
show
Bug introduced by
It seems like $this->column can also be of type array; however, Encore\Admin\Form\Field::formatName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1300
            'help'        => $this->help,
1301
            'class'       => $this->getElementClassString(),
1302
            'value'       => $this->value(),
1303
            'label'       => $this->label,
1304
            'viewClass'   => $this->getViewElementClasses(),
1305
            'column'      => $this->column,
1306
            'errorKey'    => $this->getErrorKey(),
1307
            'attributes'  => $this->formatAttributes(),
1308
            'placeholder' => $this->getPlaceholder(),
1309
        ]);
1310
    }
1311
1312
    /**
1313
     * Get view of this field.
1314
     *
1315
     * @return string
1316
     */
1317
    public function getView()
1318
    {
1319
        if (!empty($this->view)) {
1320
            return $this->view;
1321
        }
1322
1323
        $class = explode('\\', get_called_class());
1324
1325
        return 'admin::form.'.strtolower(end($class));
1326
    }
1327
1328
    /**
1329
     * Set view of current field.
1330
     *
1331
     * @param string $view
1332
     *
1333
     * @return string
1334
     */
1335
    public function setView($view)
1336
    {
1337
        $this->view = $view;
1338
1339
        return $this;
1340
    }
1341
1342
    /**
1343
     * Get script of current field.
1344
     *
1345
     * @return string
1346
     */
1347
    public function getScript()
1348
    {
1349
        return $this->script;
1350
    }
1351
1352
    /**
1353
     * Set script of current field.
1354
     *
1355
     * @param string $script
1356
     *
1357
     * @return $this
1358
     */
1359
    public function setScript($script)
1360
    {
1361
        $this->script = $script;
1362
1363
        return $this;
1364
    }
1365
1366
    /**
1367
     * To set this field should render or not.
1368
     *
1369
     * @param bool $display
1370
     *
1371
     * @return $this
1372
     */
1373
    public function setDisplay(bool $display)
1374
    {
1375
        $this->display = $display;
1376
1377
        return $this;
1378
    }
1379
1380
    /**
1381
     * If this field should render.
1382
     *
1383
     * @return bool
1384
     */
1385
    protected function shouldRender()
1386
    {
1387
        if (!$this->display) {
1388
            return false;
1389
        }
1390
1391
        return true;
1392
    }
1393
1394
    /**
1395
     * @param \Closure $callback
1396
     *
1397
     * @return \Encore\Admin\Form\Field
1398
     */
1399
    public function with(Closure $callback)
1400
    {
1401
        $this->callback = $callback;
1402
1403
        return $this;
1404
    }
1405
1406
    /**
1407
     * Render this filed.
1408
     *
1409
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1410
     */
1411
    public function render()
1412
    {
1413
        if (!$this->shouldRender()) {
1414
            return '';
1415
        }
1416
1417
        if ($this->callback instanceof Closure) {
1418
            $this->value = $this->callback->call($this->form->model(), $this->value, $this);
1419
        }
1420
1421
        $this->addRequiredAttributeFromRules();
1422
1423
        Admin::script($this->script);
1424
1425
        return view($this->getView(), $this->variables());
0 ignored issues
show
Bug Compatibility introduced by
The expression view($this->getView(), $this->variables()); of type Illuminate\View\View|Ill...\Contracts\View\Factory adds the type Illuminate\Contracts\View\Factory to the return on line 1425 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
1426
    }
1427
1428
    /**
1429
     * @return string
1430
     */
1431
    public function __toString()
1432
    {
1433
        return $this->render()->render();
0 ignored issues
show
Bug introduced by
The method render does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1434
    }
1435
}
1436