Completed
Pull Request — master (#2578)
by jxlwqq
03:05
created

Field::render()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Form;
4
5
use Encore\Admin\Admin;
6
use Encore\Admin\Form;
7
use Illuminate\Contracts\Support\Arrayable;
8
use Illuminate\Contracts\Support\Renderable;
9
use Illuminate\Support\Arr;
10
use Illuminate\Support\Facades\Validator;
11
use Illuminate\Support\Traits\Macroable;
12
13
/**
14
 * Class Field.
15
 */
16
class Field implements Renderable
17
{
18
    use Macroable;
19
20
    const FILE_DELETE_FLAG = '_file_del_';
21
22
    /**
23
     * Element id.
24
     *
25
     * @var array|string
26
     */
27
    protected $id;
28
29
    /**
30
     * Element value.
31
     *
32
     * @var mixed
33
     */
34
    protected $value;
35
36
    /**
37
     * Field original value.
38
     *
39
     * @var mixed
40
     */
41
    protected $original;
42
43
    /**
44
     * Field default value.
45
     *
46
     * @var mixed
47
     */
48
    protected $default;
49
50
    /**
51
     * Element label.
52
     *
53
     * @var string
54
     */
55
    protected $label = '';
56
57
    /**
58
     * Column name.
59
     *
60
     * @var string|array
61
     */
62
    protected $column = '';
63
64
    /**
65
     * Form element name.
66
     *
67
     * @var string
68
     */
69
    protected $elementName = [];
70
71
    /**
72
     * Form element classes.
73
     *
74
     * @var array
75
     */
76
    protected $elementClass = [];
77
78
    /**
79
     * Variables of elements.
80
     *
81
     * @var array
82
     */
83
    protected $variables = [];
84
85
    /**
86
     * Options for specify elements.
87
     *
88
     * @var array
89
     */
90
    protected $options = [];
91
92
    /**
93
     * Checked for specify elements.
94
     *
95
     * @var array
96
     */
97
    protected $checked = [];
98
99
    /**
100
     * Validation rules.
101
     *
102
     * @var string|\Closure
103
     */
104
    protected $rules = '';
105
106
    /**
107
     * @var callable
108
     */
109
    protected $validator;
110
111
    /**
112
     * Validation messages.
113
     *
114
     * @var array
115
     */
116
    protected $validationMessages = [];
117
118
    /**
119
     * Css required by this field.
120
     *
121
     * @var array
122
     */
123
    protected static $css = [];
124
125
    /**
126
     * Js required by this field.
127
     *
128
     * @var array
129
     */
130
    protected static $js = [];
131
132
    /**
133
     * Script for field.
134
     *
135
     * @var string
136
     */
137
    protected $script = '';
138
139
    /**
140
     * Element attributes.
141
     *
142
     * @var array
143
     */
144
    protected $attributes = [];
145
146
    /**
147
     * Parent form.
148
     *
149
     * @var Form
150
     */
151
    protected $form = null;
152
153
    /**
154
     * View for field to render.
155
     *
156
     * @var string
157
     */
158
    protected $view = '';
159
160
    /**
161
     * Help block.
162
     *
163
     * @var array
164
     */
165
    protected $help = [];
166
167
    /**
168
     * Key for errors.
169
     *
170
     * @var mixed
171
     */
172
    protected $errorKey;
173
174
    /**
175
     * Placeholder for this field.
176
     *
177
     * @var string|array
178
     */
179
    protected $placeholder;
180
181
    /**
182
     * Width for label and field.
183
     *
184
     * @var array
185
     */
186
    protected $width = [
187
        'label' => 2,
188
        'field' => 8,
189
    ];
190
191
    /**
192
     * If the form horizontal layout.
193
     *
194
     * @var bool
195
     */
196
    protected $horizontal = true;
197
198
    /**
199
     * column data format.
200
     *
201
     * @var \Closure
202
     */
203
    protected $customFormat = null;
204
205
    /**
206
     * @var bool
207
     */
208
    protected $display = true;
209
210
    /**
211
     * @var array
212
     */
213
    protected $labelClass = [];
214
215
    /**
216
     * Field constructor.
217
     *
218
     * @param       $column
219
     * @param array $arguments
220
     */
221
    public function __construct($column, $arguments = [])
222
    {
223
        $this->column = $column;
224
        $this->label = $this->formatLabel($arguments);
225
        $this->id = $this->formatId($column);
226
    }
227
228
    /**
229
     * Get assets required by this field.
230
     *
231
     * @return array
232
     */
233
    public static function getAssets()
234
    {
235
        return [
236
            'css' => static::$css,
237
            'js'  => static::$js,
238
        ];
239
    }
240
241
    /**
242
     * Format the field element id.
243
     *
244
     * @param string|array $column
245
     *
246
     * @return string|array
247
     */
248
    protected function formatId($column)
249
    {
250
        return str_replace('.', '_', $column);
251
    }
252
253
    /**
254
     * Format the label value.
255
     *
256
     * @param array $arguments
257
     *
258
     * @return string
259
     */
260
    protected function formatLabel($arguments = [])
261
    {
262
        $column = is_array($this->column) ? current($this->column) : $this->column;
263
264
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($column);
265
266
        return str_replace(['.', '_'], ' ', $label);
267
    }
268
269
    /**
270
     * Format the name of the field.
271
     *
272
     * @param string $column
273
     *
274
     * @return array|mixed|string
275
     */
276
    protected function formatName($column)
277
    {
278
        if (is_string($column)) {
279
            $name = explode('.', $column);
280
281
            if (count($name) == 1) {
282
                return $name[0];
283
            }
284
285
            $html = array_shift($name);
286
            foreach ($name as $piece) {
287
                $html .= "[$piece]";
288
            }
289
290
            return $html;
291
        }
292
293
        if (is_array($this->column)) {
294
            $names = [];
295
            foreach ($this->column as $key => $name) {
296
                $names[$key] = $this->formatName($name);
297
            }
298
299
            return $names;
300
        }
301
302
        return '';
303
    }
304
305
    /**
306
     * Set form element name.
307
     *
308
     * @param string $name
309
     *
310
     * @return $this
311
     *
312
     * @author Edwin Hui
313
     */
314
    public function setElementName($name)
315
    {
316
        $this->elementName = $name;
317
318
        return $this;
319
    }
320
321
    /**
322
     * Fill data to the field.
323
     *
324
     * @param array $data
325
     *
326
     * @return void
327
     */
328
    public function fill($data)
329
    {
330
        // Field value is already setted.
331
//        if (!is_null($this->value)) {
332
//            return;
333
//        }
334
335
        if (is_array($this->column)) {
336
            foreach ($this->column as $key => $column) {
337
                $this->value[$key] = array_get($data, $column);
338
            }
339
340
            return;
341
        }
342
343
        $this->value = array_get($data, $this->column);
344
        if (isset($this->customFormat) && $this->customFormat instanceof \Closure) {
345
            $this->value = call_user_func($this->customFormat, $this->value);
346
        }
347
    }
348
349
    /**
350
     * custom format form column data when edit.
351
     *
352
     * @param \Closure $call
353
     *
354
     * @return $this
355
     */
356
    public function customFormat(\Closure $call)
357
    {
358
        $this->customFormat = $call;
359
360
        return $this;
361
    }
362
363
    /**
364
     * Set original value to the field.
365
     *
366
     * @param array $data
367
     *
368
     * @return void
369
     */
370
    public function setOriginal($data)
371
    {
372
        if (is_array($this->column)) {
373
            foreach ($this->column as $key => $column) {
374
                $this->original[$key] = array_get($data, $column);
375
            }
376
377
            return;
378
        }
379
380
        $this->original = array_get($data, $this->column);
381
    }
382
383
    /**
384
     * @param Form $form
385
     *
386
     * @return $this
387
     */
388
    public function setForm(Form $form = null)
389
    {
390
        $this->form = $form;
391
392
        return $this;
393
    }
394
395
    /**
396
     * Set width for field and label.
397
     *
398
     * @param int $field
399
     * @param int $label
400
     *
401
     * @return $this
402
     */
403
    public function setWidth($field = 8, $label = 2)
404
    {
405
        $this->width = [
406
            'label' => $label,
407
            'field' => $field,
408
        ];
409
410
        return $this;
411
    }
412
413
    /**
414
     * Set the field options.
415
     *
416
     * @param array $options
417
     *
418
     * @return $this
419
     */
420 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...
421
    {
422
        if ($options instanceof Arrayable) {
423
            $options = $options->toArray();
424
        }
425
426
        $this->options = array_merge($this->options, $options);
427
428
        return $this;
429
    }
430
431
    /**
432
     * Set the field option checked.
433
     *
434
     * @param array $checked
435
     *
436
     * @return $this
437
     */
438 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...
439
    {
440
        if ($checked instanceof Arrayable) {
441
            $checked = $checked->toArray();
442
        }
443
444
        $this->checked = array_merge($this->checked, $checked);
445
446
        return $this;
447
    }
448
449
    /**
450
     * Get or set rules.
451
     *
452
     * @param null  $rules
453
     * @param array $messages
454
     *
455
     * @return $this
456
     */
457
    public function rules($rules = null, $messages = [])
458
    {
459
        if ($rules instanceof \Closure) {
460
            $this->rules = $rules;
461
        }
462
463
        if (is_array($rules)) {
464
            $thisRuleArr = array_filter(explode('|', $this->rules));
465
466
            $this->rules = array_merge($thisRuleArr, $rules);
0 ignored issues
show
Documentation Bug introduced by
It seems like array_merge($thisRuleArr, $rules) of type array is incompatible with the declared type string|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...
467
        } elseif (is_string($rules)) {
468
            $rules = array_filter(explode('|', "{$this->rules}|$rules"));
469
470
            $this->rules = implode('|', $rules);
471
        }
472
473
        $this->validationMessages = $messages;
474
475
        return $this;
476
    }
477
478
    /**
479
     * Get field validation rules.
480
     *
481
     * @return string
482
     */
483
    protected function getRules()
484
    {
485
        if ($this->rules instanceof \Closure) {
486
            return $this->rules->call($this, $this->form);
487
        }
488
489
        return $this->rules;
490
    }
491
492
    /**
493
     * Remove a specific rule by keyword.
494
     *
495
     * @param string $rule
496
     *
497
     * @return void
498
     */
499
    protected function removeRule($rule)
500
    {
501
        if (!is_string($this->rules)) {
502
            return;
503
        }
504
505
        $pattern = "/{$rule}[^\|]?(\||$)/";
506
        $this->rules = preg_replace($pattern, '', $this->rules, -1);
507
    }
508
509
    /**
510
     * Set field validator.
511
     *
512
     * @param callable $validator
513
     *
514
     * @return $this
515
     */
516
    public function validator(callable $validator)
517
    {
518
        $this->validator = $validator;
519
520
        return $this;
521
    }
522
523
    /**
524
     * Get key for error message.
525
     *
526
     * @return string
527
     */
528
    public function getErrorKey()
529
    {
530
        return $this->errorKey ?: $this->column;
531
    }
532
533
    /**
534
     * Set key for error message.
535
     *
536
     * @param string $key
537
     *
538
     * @return $this
539
     */
540
    public function setErrorKey($key)
541
    {
542
        $this->errorKey = $key;
543
544
        return $this;
545
    }
546
547
    /**
548
     * Set or get value of the field.
549
     *
550
     * @param null $value
551
     *
552
     * @return mixed
553
     */
554 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...
555
    {
556
        if (is_null($value)) {
557
            return is_null($this->value) ? $this->getDefault() : $this->value;
558
        }
559
560
        $this->value = $value;
561
562
        return $this;
563
    }
564
565
    /**
566
     * Set default value for field.
567
     *
568
     * @param $default
569
     *
570
     * @return $this
571
     */
572
    public function default($default)
573
    {
574
        $this->default = $default;
575
576
        return $this;
577
    }
578
579
    /**
580
     * Get default value.
581
     *
582
     * @return mixed
583
     */
584
    public function getDefault()
585
    {
586
        if ($this->default instanceof \Closure) {
587
            return call_user_func($this->default, $this->form);
588
        }
589
590
        return $this->default;
591
    }
592
593
    /**
594
     * Set help block for current field.
595
     *
596
     * @param string $text
597
     * @param string $icon
598
     *
599
     * @return $this
600
     */
601
    public function help($text = '', $icon = 'fa-info-circle')
602
    {
603
        $this->help = compact('text', 'icon');
604
605
        return $this;
606
    }
607
608
    /**
609
     * Get column of the field.
610
     *
611
     * @return string|array
612
     */
613
    public function column()
614
    {
615
        return $this->column;
616
    }
617
618
    /**
619
     * Get label of the field.
620
     *
621
     * @return string
622
     */
623
    public function label()
624
    {
625
        return $this->label;
626
    }
627
628
    /**
629
     * Get original value of the field.
630
     *
631
     * @return mixed
632
     */
633
    public function original()
634
    {
635
        return $this->original;
636
    }
637
638
    /**
639
     * Get validator for this field.
640
     *
641
     * @param array $input
642
     *
643
     * @return bool|Validator
644
     */
645
    public function getValidator(array $input)
646
    {
647
        if ($this->validator) {
648
            return $this->validator->call($this, $input);
649
        }
650
651
        $rules = $attributes = [];
652
653
        if (!$fieldRules = $this->getRules()) {
654
            return false;
655
        }
656
657
        if (is_string($this->column)) {
658
            if (!array_has($input, $this->column)) {
659
                return false;
660
            }
661
662
            $input = $this->sanitizeInput($input, $this->column);
663
664
            $rules[$this->column] = $fieldRules;
665
            $attributes[$this->column] = $this->label;
666
        }
667
668
        if (is_array($this->column)) {
669
            foreach ($this->column as $key => $column) {
670
                if (!array_key_exists($column, $input)) {
671
                    continue;
672
                }
673
                $input[$column.$key] = array_get($input, $column);
674
                $rules[$column.$key] = $fieldRules;
675
                $attributes[$column.$key] = $this->label."[$column]";
676
            }
677
        }
678
679
        return Validator::make($input, $rules, $this->validationMessages, $attributes);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \Illuminate\Suppo...Messages, $attributes); (Illuminate\Contracts\Validation\Validator) is incompatible with the return type documented by Encore\Admin\Form\Field::getValidator of type boolean|Illuminate\Support\Facades\Validator.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
680
    }
681
682
    /**
683
     * Sanitize input data.
684
     *
685
     * @param array  $input
686
     * @param string $column
687
     *
688
     * @return array
689
     */
690
    protected function sanitizeInput($input, $column)
691
    {
692
        if ($this instanceof Field\MultipleSelect) {
693
            $value = array_get($input, $column);
694
            array_set($input, $column, array_filter($value));
695
        }
696
697
        return $input;
698
    }
699
700
    /**
701
     * Add html attributes to elements.
702
     *
703
     * @param array|string $attribute
704
     * @param mixed        $value
705
     *
706
     * @return $this
707
     */
708
    public function attribute($attribute, $value = null)
709
    {
710
        if (is_array($attribute)) {
711
            $this->attributes = array_merge($this->attributes, $attribute);
712
        } else {
713
            $this->attributes[$attribute] = (string) $value;
714
        }
715
716
        return $this;
717
    }
718
719
    /**
720
     * Specifies a regular expression against which to validate the value of the input.
721
     *
722
     * @param string $regexp
723
     *
724
     * @return Field
725
     */
726
    public function pattern($regexp)
727
    {
728
        return $this->attribute('pattern', $regexp);
729
    }
730
    
731
    /**
732
     * set the input filed required
733
     *
734
     * @return Field
735
     */
736
    public function required()
737
    {
738
        return $this->attribute('required', true);
739
    }
740
741
    /**
742
     * Set the field automatically get focus.
743
     *
744
     * @return Field
745
     */
746
    public function autofocus()
747
    {
748
        return $this->attribute('autofocus', true);
749
    }
750
751
    /**
752
     * Set the field as readonly mode.
753
     *
754
     * @return Field
755
     */
756
    public function readOnly()
757
    {
758
        return $this->attribute('readonly', true);
759
    }
760
761
    /**
762
     * Set field as disabled.
763
     *
764
     * @return Field
765
     */
766
    public function disable()
767
    {
768
        return $this->attribute('disabled', true);
769
    }
770
771
    /**
772
     * Set field placeholder.
773
     *
774
     * @param string $placeholder
775
     *
776
     * @return Field
777
     */
778
    public function placeholder($placeholder = '')
779
    {
780
        $this->placeholder = $placeholder;
781
782
        return $this;
783
    }
784
785
    /**
786
     * Get placeholder.
787
     *
788
     * @return string
789
     */
790
    public function getPlaceholder()
791
    {
792
        return $this->placeholder ?: trans('admin.input').' '.$this->label;
793
    }
794
795
    /**
796
     * Prepare for a field value before update or insert.
797
     *
798
     * @param $value
799
     *
800
     * @return mixed
801
     */
802
    public function prepare($value)
803
    {
804
        return $value;
805
    }
806
807
    /**
808
     * Format the field attributes.
809
     *
810
     * @return string
811
     */
812
    protected function formatAttributes()
813
    {
814
        $html = [];
815
816
        foreach ($this->attributes as $name => $value) {
817
            $html[] = $name.'="'.e($value).'"';
818
        }
819
820
        return implode(' ', $html);
821
    }
822
823
    /**
824
     * @return $this
825
     */
826
    public function disableHorizontal()
827
    {
828
        $this->horizontal = false;
829
830
        return $this;
831
    }
832
833
    /**
834
     * @return array
835
     */
836
    public function getViewElementClasses()
837
    {
838
        if ($this->horizontal) {
839
            return [
840
                'label'      => "col-sm-{$this->width['label']} {$this->getLabelClass()}",
841
                'field'      => "col-sm-{$this->width['field']}",
842
                'form-group' => 'form-group ',
843
            ];
844
        }
845
846
        return ['label' => "{$this->getLabelClass()}", 'field' => '', 'form-group' => ''];
847
    }
848
849
    /**
850
     * Set form element class.
851
     *
852
     * @param string|array $class
853
     *
854
     * @return $this
855
     */
856
    public function setElementClass($class)
857
    {
858
        $this->elementClass = (array) $class;
859
860
        return $this;
861
    }
862
863
    /**
864
     * Get element class.
865
     *
866
     * @return array
867
     */
868
    protected function getElementClass()
869
    {
870
        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...
871
            $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...
872
873
            $this->elementClass = (array) str_replace(['[', ']'], '_', $name);
874
        }
875
876
        return $this->elementClass;
877
    }
878
879
    /**
880
     * Get element class string.
881
     *
882
     * @return mixed
883
     */
884 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...
885
    {
886
        $elementClass = $this->getElementClass();
887
888
        if (Arr::isAssoc($elementClass)) {
889
            $classes = [];
890
891
            foreach ($elementClass as $index => $class) {
892
                $classes[$index] = is_array($class) ? implode(' ', $class) : $class;
893
            }
894
895
            return $classes;
896
        }
897
898
        return implode(' ', $elementClass);
899
    }
900
901
    /**
902
     * Get element class selector.
903
     *
904
     * @return string|array
905
     */
906 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...
907
    {
908
        $elementClass = $this->getElementClass();
909
910
        if (Arr::isAssoc($elementClass)) {
911
            $classes = [];
912
913
            foreach ($elementClass as $index => $class) {
914
                $classes[$index] = '.'.(is_array($class) ? implode('.', $class) : $class);
915
            }
916
917
            return $classes;
918
        }
919
920
        return '.'.implode('.', $elementClass);
921
    }
922
923
    /**
924
     * Add the element class.
925
     *
926
     * @param $class
927
     *
928
     * @return $this
929
     */
930
    public function addElementClass($class)
931
    {
932
        if (is_array($class) || is_string($class)) {
933
            $this->elementClass = array_merge($this->elementClass, (array) $class);
934
935
            $this->elementClass = array_unique($this->elementClass);
936
        }
937
938
        return $this;
939
    }
940
941
    /**
942
     * Remove element class.
943
     *
944
     * @param $class
945
     *
946
     * @return $this
947
     */
948
    public function removeElementClass($class)
949
    {
950
        $delClass = [];
951
952
        if (is_string($class) || is_array($class)) {
953
            $delClass = (array) $class;
954
        }
955
956
        foreach ($delClass as $del) {
957
            if (($key = array_search($del, $this->elementClass))) {
958
                unset($this->elementClass[$key]);
959
            }
960
        }
961
962
        return $this;
963
    }
964
965
    /**
966
     * Add variables to field view.
967
     *
968
     * @param array $variables
969
     *
970
     * @return $this
971
     */
972
    protected function addVariables(array $variables = [])
973
    {
974
        $this->variables = array_merge($this->variables, $variables);
975
976
        return $this;
977
    }
978
979
    /**
980
     * @return string
981
     */
982
    public function getLabelClass()
983
    : string
984
    {
985
        return implode(' ', $this->labelClass);
986
    }
987
988
    /**
989
     * @param array $labelClass
990
     *
991
     * @return self
992
     */
993
    public function setLabelClass(array $labelClass)
994
    : self
995
    {
996
        $this->labelClass = $labelClass;
997
998
        return $this;
999
    }
1000
1001
    /**
1002
     * Get the view variables of this field.
1003
     *
1004
     * @return array
1005
     */
1006
    public function variables()
1007
    {
1008
        return array_merge($this->variables, [
1009
            'id'          => $this->id,
1010
            '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...
1011
            'help'        => $this->help,
1012
            'class'       => $this->getElementClassString(),
1013
            'value'       => $this->value(),
1014
            'label'       => $this->label,
1015
            'viewClass'   => $this->getViewElementClasses(),
1016
            'column'      => $this->column,
1017
            'errorKey'    => $this->getErrorKey(),
1018
            'attributes'  => $this->formatAttributes(),
1019
            'placeholder' => $this->getPlaceholder(),
1020
        ]);
1021
    }
1022
1023
    /**
1024
     * Get view of this field.
1025
     *
1026
     * @return string
1027
     */
1028
    public function getView()
1029
    {
1030
        if (!empty($this->view)) {
1031
            return $this->view;
1032
        }
1033
1034
        $class = explode('\\', get_called_class());
1035
1036
        return 'admin::form.'.strtolower(end($class));
1037
    }
1038
1039
    /**
1040
     * Get script of current field.
1041
     *
1042
     * @return string
1043
     */
1044
    public function getScript()
1045
    {
1046
        return $this->script;
1047
    }
1048
1049
    /**
1050
     * If this field should render.
1051
     *
1052
     * @return bool
1053
     */
1054
    protected function shouldRender()
1055
    {
1056
        if (!$this->display) {
1057
            return false;
1058
        }
1059
1060
        return true;
1061
    }
1062
1063
    /**
1064
     * Render this filed.
1065
     *
1066
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1067
     */
1068
    public function render()
1069
    {
1070
        if (!$this->shouldRender()) {
1071
            return '';
1072
        }
1073
1074
        Admin::script($this->script);
1075
1076
        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 1076 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
1077
    }
1078
1079
    /**
1080
     * @return string
1081
     */
1082
    public function __toString()
1083
    {
1084
        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...
1085
    }
1086
}
1087