Completed
Push — master ( c7a24e...b9d51d )
by Song
03:23
created

Field::setDisplay()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 6
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 View Code Duplication
    public function fill($data)
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...
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
402
        $this->formatValue();
403
    }
404
405
    /**
406
     * Format value by passing custom formater.
407
     */
408
    protected function formatValue()
409
    {
410
        if (isset($this->customFormat) && $this->customFormat instanceof \Closure) {
411
            $this->value = call_user_func($this->customFormat, $this->value);
412
        }
413
    }
414
415
    /**
416
     * custom format form column data when edit.
417
     *
418
     * @param \Closure $call
419
     *
420
     * @return $this
421
     */
422
    public function customFormat(\Closure $call)
423
    {
424
        $this->customFormat = $call;
425
426
        return $this;
427
    }
428
429
    /**
430
     * Set original value to the field.
431
     *
432
     * @param array $data
433
     *
434
     * @return void
435
     */
436 View Code Duplication
    public function setOriginal($data)
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...
437
    {
438
        if (is_array($this->column)) {
439
            foreach ($this->column as $key => $column) {
440
                $this->original[$key] = Arr::get($data, $column);
441
            }
442
443
            return;
444
        }
445
446
        $this->original = Arr::get($data, $this->column);
447
    }
448
449
    /**
450
     * @param Form $form
451
     *
452
     * @return $this
453
     */
454
    public function setForm(Form $form = null)
455
    {
456
        $this->form = $form;
457
458
        return $this;
459
    }
460
461
    /**
462
     * Set width for field and label.
463
     *
464
     * @param int $field
465
     * @param int $label
466
     *
467
     * @return $this
468
     */
469
    public function setWidth($field = 8, $label = 2)
470
    {
471
        $this->width = [
472
            'label' => $label,
473
            'field' => $field,
474
        ];
475
476
        return $this;
477
    }
478
479
    /**
480
     * Set the field options.
481
     *
482
     * @param array $options
483
     *
484
     * @return $this
485
     */
486 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...
487
    {
488
        if ($options instanceof Arrayable) {
489
            $options = $options->toArray();
490
        }
491
492
        $this->options = array_merge($this->options, $options);
493
494
        return $this;
495
    }
496
497
    /**
498
     * Set the field option checked.
499
     *
500
     * @param array $checked
501
     *
502
     * @return $this
503
     */
504 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...
505
    {
506
        if ($checked instanceof Arrayable) {
507
            $checked = $checked->toArray();
508
        }
509
510
        $this->checked = array_merge($this->checked, $checked);
511
512
        return $this;
513
    }
514
515
    /**
516
     * Add `required` attribute to current field if has required rule,
517
     * except file and image fields.
518
     *
519
     * @param array $rules
520
     */
521
    protected function addRequiredAttribute($rules)
522
    {
523
        if (!is_array($rules)) {
524
            return;
525
        }
526
527
        if (!in_array('required', $rules)) {
528
            return;
529
        }
530
531
        if ($this instanceof Form\Field\MultipleFile
532
            || $this instanceof Form\Field\File) {
533
            return;
534
        }
535
536
        $this->required();
537
    }
538
539
    /**
540
     * If has `required` rule, add required attribute to this field.
541
     */
542
    protected function addRequiredAttributeFromRules()
543
    {
544
        if (is_null($this->data)) {
545
            // Create page
546
            $rules = $this->creationRules ?: $this->rules;
547
        } else {
548
            // Update page
549
            $rules = $this->updateRules ?: $this->rules;
550
        }
551
552
        $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...
553
    }
554
555
    /**
556
     * Format validation rules.
557
     *
558
     * @param array|string $rules
559
     *
560
     * @return array
561
     */
562
    protected function formatRules($rules)
563
    {
564
        if (is_string($rules)) {
565
            $rules = array_filter(explode('|', $rules));
566
        }
567
568
        return array_filter((array) $rules);
569
    }
570
571
    /**
572
     * @param string|array|Closure $input
573
     * @param string|array         $original
574
     *
575
     * @return array|Closure
576
     */
577
    protected function mergeRules($input, $original)
578
    {
579
        if ($input instanceof Closure) {
580
            $rules = $input;
581
        } else {
582
            if (!empty($original)) {
583
                $original = $this->formatRules($original);
584
            }
585
586
            $rules = array_merge($original, $this->formatRules($input));
587
        }
588
589
        return $rules;
590
    }
591
592
    /**
593
     * Set the validation rules for the field.
594
     *
595
     * @param array|callable|string $rules
596
     * @param array                 $messages
597
     *
598
     * @return $this
599
     */
600 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...
601
    {
602
        $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...
603
604
        $this->setValidationMessages('default', $messages);
605
606
        return $this;
607
    }
608
609
    /**
610
     * Set the update validation rules for the field.
611
     *
612
     * @param array|callable|string $rules
613
     * @param array                 $messages
614
     *
615
     * @return $this
616
     */
617 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...
618
    {
619
        $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...
620
621
        $this->setValidationMessages('update', $messages);
622
623
        return $this;
624
    }
625
626
    /**
627
     * Set the creation validation rules for the field.
628
     *
629
     * @param array|callable|string $rules
630
     * @param array                 $messages
631
     *
632
     * @return $this
633
     */
634
    public function creationRules($rules = null, $messages = [])
635
    {
636
        $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...
637
638
        $this->setValidationMessages('creation', $messages);
639
640
        return $this;
641
    }
642
643
    /**
644
     * Set validation messages for column.
645
     *
646
     * @param string $key
647
     * @param array  $messages
648
     *
649
     * @return $this
650
     */
651
    public function setValidationMessages($key, array $messages)
652
    {
653
        $this->validationMessages[$key] = $messages;
654
655
        return $this;
656
    }
657
658
    /**
659
     * Get validation messages for the field.
660
     *
661
     * @return array|mixed
662
     */
663
    public function getValidationMessages()
664
    {
665
        // Default validation message.
666
        $messages = $this->validationMessages['default'] ?? [];
667
668
        if (request()->isMethod('POST')) {
669
            $messages = $this->validationMessages['creation'] ?? $messages;
670
        } elseif (request()->isMethod('PUT')) {
671
            $messages = $this->validationMessages['update'] ?? $messages;
672
        }
673
674
        return $messages;
675
    }
676
677
    /**
678
     * Get field validation rules.
679
     *
680
     * @return string
681
     */
682
    protected function getRules()
683
    {
684
        if (request()->isMethod('POST')) {
685
            $rules = $this->creationRules ?: $this->rules;
686
        } elseif (request()->isMethod('PUT')) {
687
            $rules = $this->updateRules ?: $this->rules;
688
        } else {
689
            $rules = $this->rules;
690
        }
691
692
        if ($rules instanceof \Closure) {
693
            $rules = $rules->call($this, $this->form);
694
        }
695
696
        if (is_string($rules)) {
697
            $rules = array_filter(explode('|', $rules));
698
        }
699
700
        if (!$this->form) {
701
            return $rules;
702
        }
703
704
        if (!$id = $this->form->model()->getKey()) {
705
            return $rules;
706
        }
707
708
        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...
709
            if (is_string($rule)) {
710
                $rule = str_replace('{{id}}', $id, $rule);
711
            }
712
        }
713
714
        return $rules;
715
    }
716
717
    /**
718
     * Remove a specific rule by keyword.
719
     *
720
     * @param string $rule
721
     *
722
     * @return void
723
     */
724
    protected function removeRule($rule)
725
    {
726
        if (!is_string($this->rules)) {
727
            return;
728
        }
729
730
        $pattern = "/{$rule}[^\|]?(\||$)/";
731
        $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...
732
    }
733
734
    /**
735
     * Set field validator.
736
     *
737
     * @param callable $validator
738
     *
739
     * @return $this
740
     */
741
    public function validator(callable $validator)
742
    {
743
        $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...
744
745
        return $this;
746
    }
747
748
    /**
749
     * Get key for error message.
750
     *
751
     * @return string
752
     */
753
    public function getErrorKey()
754
    {
755
        return $this->errorKey ?: $this->column;
756
    }
757
758
    /**
759
     * Set key for error message.
760
     *
761
     * @param string $key
762
     *
763
     * @return $this
764
     */
765
    public function setErrorKey($key)
766
    {
767
        $this->errorKey = $key;
768
769
        return $this;
770
    }
771
772
    /**
773
     * Set or get value of the field.
774
     *
775
     * @param null $value
776
     *
777
     * @return mixed
778
     */
779 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...
780
    {
781
        if (is_null($value)) {
782
            return is_null($this->value) ? $this->getDefault() : $this->value;
783
        }
784
785
        $this->value = $value;
786
787
        return $this;
788
    }
789
790
    /**
791
     * Set or get data.
792
     *
793
     * @param array $data
794
     *
795
     * @return $this
796
     */
797
    public function data(array $data = null)
798
    {
799
        if (is_null($data)) {
800
            return $this->data;
801
        }
802
803
        $this->data = $data;
804
805
        return $this;
806
    }
807
808
    /**
809
     * Set default value for field.
810
     *
811
     * @param $default
812
     *
813
     * @return $this
814
     */
815
    public function default($default)
816
    {
817
        $this->default = $default;
818
819
        return $this;
820
    }
821
822
    /**
823
     * Get default value.
824
     *
825
     * @return mixed
826
     */
827
    public function getDefault()
828
    {
829
        if ($this->default instanceof \Closure) {
830
            return call_user_func($this->default, $this->form);
831
        }
832
833
        return $this->default;
834
    }
835
836
    /**
837
     * Set help block for current field.
838
     *
839
     * @param string $text
840
     * @param string $icon
841
     *
842
     * @return $this
843
     */
844
    public function help($text = '', $icon = 'fa-info-circle')
845
    {
846
        $this->help = compact('text', 'icon');
847
848
        return $this;
849
    }
850
851
    /**
852
     * Get column of the field.
853
     *
854
     * @return string|array
855
     */
856
    public function column()
857
    {
858
        return $this->column;
859
    }
860
861
    /**
862
     * Get label of the field.
863
     *
864
     * @return string
865
     */
866
    public function label()
867
    {
868
        return $this->label;
869
    }
870
871
    /**
872
     * Get original value of the field.
873
     *
874
     * @return mixed
875
     */
876
    public function original()
877
    {
878
        return $this->original;
879
    }
880
881
    /**
882
     * Get validator for this field.
883
     *
884
     * @param array $input
885
     *
886
     * @return bool|\Illuminate\Contracts\Validation\Validator|mixed
887
     */
888
    public function getValidator(array $input)
889
    {
890
        if ($this->validator) {
891
            return $this->validator->call($this, $input);
892
        }
893
894
        $rules = $attributes = [];
895
896
        if (!$fieldRules = $this->getRules()) {
897
            return false;
898
        }
899
900
        if (is_string($this->column)) {
901
            if (!Arr::has($input, $this->column)) {
902
                return false;
903
            }
904
905
            $input = $this->sanitizeInput($input, $this->column);
906
907
            $rules[$this->column] = $fieldRules;
908
            $attributes[$this->column] = $this->label;
909
        }
910
911
        if (is_array($this->column)) {
912
            foreach ($this->column as $key => $column) {
913
                if (!array_key_exists($column, $input)) {
914
                    continue;
915
                }
916
                $input[$column.$key] = Arr::get($input, $column);
917
                $rules[$column.$key] = $fieldRules;
918
                $attributes[$column.$key] = $this->label."[$column]";
919
            }
920
        }
921
922
        return \validator($input, $rules, $this->getValidationMessages(), $attributes);
923
    }
924
925
    /**
926
     * Sanitize input data.
927
     *
928
     * @param array  $input
929
     * @param string $column
930
     *
931
     * @return array
932
     */
933
    protected function sanitizeInput($input, $column)
934
    {
935
        if ($this instanceof Field\MultipleSelect) {
936
            $value = Arr::get($input, $column);
937
            Arr::set($input, $column, array_filter($value));
938
        }
939
940
        return $input;
941
    }
942
943
    /**
944
     * Add html attributes to elements.
945
     *
946
     * @param array|string $attribute
947
     * @param mixed        $value
948
     *
949
     * @return $this
950
     */
951
    public function attribute($attribute, $value = null)
952
    {
953
        if (is_array($attribute)) {
954
            $this->attributes = array_merge($this->attributes, $attribute);
955
        } else {
956
            $this->attributes[$attribute] = (string) $value;
957
        }
958
959
        return $this;
960
    }
961
962
    /**
963
     * Specifies a regular expression against which to validate the value of the input.
964
     *
965
     * @param string $regexp
966
     *
967
     * @return Field
968
     */
969
    public function pattern($regexp)
970
    {
971
        return $this->attribute('pattern', $regexp);
972
    }
973
974
    /**
975
     * set the input filed required.
976
     *
977
     * @param bool $isLabelAsterisked
978
     *
979
     * @return Field
980
     */
981
    public function required($isLabelAsterisked = true)
982
    {
983
        if ($isLabelAsterisked) {
984
            $this->setLabelClass(['asterisk']);
985
        }
986
987
        return $this->attribute('required', true);
988
    }
989
990
    /**
991
     * Set the field automatically get focus.
992
     *
993
     * @return Field
994
     */
995
    public function autofocus()
996
    {
997
        return $this->attribute('autofocus', true);
998
    }
999
1000
    /**
1001
     * Set the field as readonly mode.
1002
     *
1003
     * @return Field
1004
     */
1005
    public function readonly()
1006
    {
1007
        return $this->attribute('readonly', true);
1008
    }
1009
1010
    /**
1011
     * Set field as disabled.
1012
     *
1013
     * @return Field
1014
     */
1015
    public function disable()
1016
    {
1017
        return $this->attribute('disabled', true);
1018
    }
1019
1020
    /**
1021
     * Set field placeholder.
1022
     *
1023
     * @param string $placeholder
1024
     *
1025
     * @return Field
1026
     */
1027
    public function placeholder($placeholder = '')
1028
    {
1029
        $this->placeholder = $placeholder;
1030
1031
        return $this;
1032
    }
1033
1034
    /**
1035
     * Get placeholder.
1036
     *
1037
     * @return string
1038
     */
1039
    public function getPlaceholder()
1040
    {
1041
        return $this->placeholder ?: trans('admin.input').' '.$this->label;
1042
    }
1043
1044
    /**
1045
     * Prepare for a field value before update or insert.
1046
     *
1047
     * @param $value
1048
     *
1049
     * @return mixed
1050
     */
1051
    public function prepare($value)
1052
    {
1053
        return $value;
1054
    }
1055
1056
    /**
1057
     * Format the field attributes.
1058
     *
1059
     * @return string
1060
     */
1061
    protected function formatAttributes()
1062
    {
1063
        $html = [];
1064
1065
        foreach ($this->attributes as $name => $value) {
1066
            $html[] = $name.'="'.e($value).'"';
1067
        }
1068
1069
        return implode(' ', $html);
1070
    }
1071
1072
    /**
1073
     * @return $this
1074
     */
1075
    public function disableHorizontal()
1076
    {
1077
        $this->horizontal = false;
1078
1079
        return $this;
1080
    }
1081
1082
    /**
1083
     * @return array
1084
     */
1085
    public function getViewElementClasses()
1086
    {
1087
        if ($this->horizontal) {
1088
            return [
1089
                'label'      => "col-sm-{$this->width['label']} {$this->getLabelClass()}",
1090
                'field'      => "col-sm-{$this->width['field']}",
1091
                'form-group' => $this->getGroupClass(true),
1092
            ];
1093
        }
1094
1095
        return ['label' => "{$this->getLabelClass()}", 'field' => '', 'form-group' => ''];
1096
    }
1097
1098
    /**
1099
     * Set form element class.
1100
     *
1101
     * @param string|array $class
1102
     *
1103
     * @return $this
1104
     */
1105
    public function setElementClass($class)
1106
    {
1107
        $this->elementClass = array_merge($this->elementClass, (array) $class);
1108
1109
        return $this;
1110
    }
1111
1112
    /**
1113
     * Get element class.
1114
     *
1115
     * @return array
1116
     */
1117
    protected function getElementClass()
1118
    {
1119
        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...
1120
            $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...
1121
1122
            $this->elementClass = (array) str_replace(['[', ']'], '_', $name);
1123
        }
1124
1125
        return $this->elementClass;
1126
    }
1127
1128
    /**
1129
     * Get element class string.
1130
     *
1131
     * @return mixed
1132
     */
1133 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...
1134
    {
1135
        $elementClass = $this->getElementClass();
1136
1137
        if (Arr::isAssoc($elementClass)) {
1138
            $classes = [];
1139
1140
            foreach ($elementClass as $index => $class) {
1141
                $classes[$index] = is_array($class) ? implode(' ', $class) : $class;
1142
            }
1143
1144
            return $classes;
1145
        }
1146
1147
        return implode(' ', $elementClass);
1148
    }
1149
1150
    /**
1151
     * Get element class selector.
1152
     *
1153
     * @return string|array
1154
     */
1155 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...
1156
    {
1157
        $elementClass = $this->getElementClass();
1158
1159
        if (Arr::isAssoc($elementClass)) {
1160
            $classes = [];
1161
1162
            foreach ($elementClass as $index => $class) {
1163
                $classes[$index] = '.'.(is_array($class) ? implode('.', $class) : $class);
1164
            }
1165
1166
            return $classes;
1167
        }
1168
1169
        return '.'.implode('.', $elementClass);
1170
    }
1171
1172
    /**
1173
     * Add the element class.
1174
     *
1175
     * @param $class
1176
     *
1177
     * @return $this
1178
     */
1179
    public function addElementClass($class)
1180
    {
1181
        if (is_array($class) || is_string($class)) {
1182
            $this->elementClass = array_merge($this->elementClass, (array) $class);
1183
1184
            $this->elementClass = array_unique($this->elementClass);
1185
        }
1186
1187
        return $this;
1188
    }
1189
1190
    /**
1191
     * Remove element class.
1192
     *
1193
     * @param $class
1194
     *
1195
     * @return $this
1196
     */
1197
    public function removeElementClass($class)
1198
    {
1199
        $delClass = [];
1200
1201
        if (is_string($class) || is_array($class)) {
1202
            $delClass = (array) $class;
1203
        }
1204
1205
        foreach ($delClass as $del) {
1206
            if (($key = array_search($del, $this->elementClass)) !== false) {
1207
                unset($this->elementClass[$key]);
1208
            }
1209
        }
1210
1211
        return $this;
1212
    }
1213
1214
    /**
1215
     * Set form group class.
1216
     *
1217
     * @param string|array $class
1218
     *
1219
     * @return $this
1220
     */
1221
    public function setGroupClass($class)
1222
    : self
1223
    {
1224
        if (is_array($class)) {
1225
            $this->groupClass = array_merge($this->groupClass, $class);
1226
        } else {
1227
            array_push($this->groupClass, $class);
1228
        }
1229
1230
        return $this;
1231
    }
1232
1233
    /**
1234
     * Get element class.
1235
     *
1236
     * @param bool $default
1237
     *
1238
     * @return string
1239
     */
1240
    protected function getGroupClass($default = false)
1241
    : string
1242
    {
1243
        return ($default ? 'form-group ' : '').implode(' ', array_filter($this->groupClass));
1244
    }
1245
1246
    /**
1247
     * reset field className.
1248
     *
1249
     * @param string $className
1250
     * @param string $resetClassName
1251
     *
1252
     * @return $this
1253
     */
1254
    public function resetElementClassName(string $className, string $resetClassName)
1255
    {
1256
        if (($key = array_search($className, $this->getElementClass())) !== false) {
1257
            $this->elementClass[$key] = $resetClassName;
1258
        }
1259
1260
        return $this;
1261
    }
1262
1263
    /**
1264
     * Add variables to field view.
1265
     *
1266
     * @param array $variables
1267
     *
1268
     * @return $this
1269
     */
1270
    protected function addVariables(array $variables = [])
1271
    {
1272
        $this->variables = array_merge($this->variables, $variables);
1273
1274
        return $this;
1275
    }
1276
1277
    /**
1278
     * @return string
1279
     */
1280
    public function getLabelClass()
1281
    : string
1282
    {
1283
        return implode(' ', $this->labelClass);
1284
    }
1285
1286
    /**
1287
     * @param array $labelClass
1288
     *
1289
     * @return self
1290
     */
1291
    public function setLabelClass(array $labelClass)
1292
    : self
1293
    {
1294
        $this->labelClass = $labelClass;
1295
1296
        return $this;
1297
    }
1298
1299
    /**
1300
     * Get the view variables of this field.
1301
     *
1302
     * @return array
1303
     */
1304
    public function variables()
1305
    {
1306
        return array_merge($this->variables, [
1307
            'id'          => $this->id,
1308
            '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...
1309
            'help'        => $this->help,
1310
            'class'       => $this->getElementClassString(),
1311
            'value'       => $this->value(),
1312
            'label'       => $this->label,
1313
            'viewClass'   => $this->getViewElementClasses(),
1314
            'column'      => $this->column,
1315
            'errorKey'    => $this->getErrorKey(),
1316
            'attributes'  => $this->formatAttributes(),
1317
            'placeholder' => $this->getPlaceholder(),
1318
        ]);
1319
    }
1320
1321
    /**
1322
     * Get view of this field.
1323
     *
1324
     * @return string
1325
     */
1326
    public function getView()
1327
    {
1328
        if (!empty($this->view)) {
1329
            return $this->view;
1330
        }
1331
1332
        $class = explode('\\', get_called_class());
1333
1334
        return 'admin::form.'.strtolower(end($class));
1335
    }
1336
1337
    /**
1338
     * Set view of current field.
1339
     *
1340
     * @param string $view
1341
     *
1342
     * @return string
1343
     */
1344
    public function setView($view)
1345
    {
1346
        $this->view = $view;
1347
1348
        return $this;
1349
    }
1350
1351
    /**
1352
     * Get script of current field.
1353
     *
1354
     * @return string
1355
     */
1356
    public function getScript()
1357
    {
1358
        return $this->script;
1359
    }
1360
1361
    /**
1362
     * Set script of current field.
1363
     *
1364
     * @param string $script
1365
     *
1366
     * @return $this
1367
     */
1368
    public function setScript($script)
1369
    {
1370
        $this->script = $script;
1371
1372
        return $this;
1373
    }
1374
1375
    /**
1376
     * To set this field should render or not.
1377
     *
1378
     * @param bool $display
1379
     *
1380
     * @return $this
1381
     */
1382
    public function setDisplay(bool $display)
1383
    {
1384
        $this->display = $display;
1385
1386
        return $this;
1387
    }
1388
1389
    /**
1390
     * If this field should render.
1391
     *
1392
     * @return bool
1393
     */
1394
    protected function shouldRender()
1395
    {
1396
        if (!$this->display) {
1397
            return false;
1398
        }
1399
1400
        return true;
1401
    }
1402
1403
    /**
1404
     * @param \Closure $callback
1405
     *
1406
     * @return \Encore\Admin\Form\Field
1407
     */
1408
    public function with(Closure $callback)
1409
    {
1410
        $this->callback = $callback;
1411
1412
        return $this;
1413
    }
1414
1415
    /**
1416
     * Render this filed.
1417
     *
1418
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1419
     */
1420
    public function render()
1421
    {
1422
        if (!$this->shouldRender()) {
1423
            return '';
1424
        }
1425
1426
        if ($this->callback instanceof Closure) {
1427
            $this->value = $this->callback->call($this->form->model(), $this->value, $this);
1428
        }
1429
1430
        $this->addRequiredAttributeFromRules();
1431
1432
        Admin::script($this->script);
1433
1434
        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 1434 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
1435
    }
1436
1437
    /**
1438
     * @return string
1439
     */
1440
    public function __toString()
1441
    {
1442
        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...
1443
    }
1444
}
1445