Completed
Push — master ( fcb08f...027370 )
by Song
04:02 queued 01:46
created

Field::rules()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 10
nop 2
dl 0
loc 22
rs 8.9457
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
     * Data of all original columns of value.
38
     *
39
     * @var mixed
40
     */
41
    protected $data;
42
43
    /**
44
     * Field original value.
45
     *
46
     * @var mixed
47
     */
48
    protected $original;
49
50
    /**
51
     * Field default value.
52
     *
53
     * @var mixed
54
     */
55
    protected $default;
56
57
    /**
58
     * Element label.
59
     *
60
     * @var string
61
     */
62
    protected $label = '';
63
64
    /**
65
     * Column name.
66
     *
67
     * @var string|array
68
     */
69
    protected $column = '';
70
71
    /**
72
     * Form element name.
73
     *
74
     * @var string
75
     */
76
    protected $elementName = [];
77
78
    /**
79
     * Form element classes.
80
     *
81
     * @var array
82
     */
83
    protected $elementClass = [];
84
85
    /**
86
     * Variables of elements.
87
     *
88
     * @var array
89
     */
90
    protected $variables = [];
91
92
    /**
93
     * Options for specify elements.
94
     *
95
     * @var array
96
     */
97
    protected $options = [];
98
99
    /**
100
     * Checked for specify elements.
101
     *
102
     * @var array
103
     */
104
    protected $checked = [];
105
106
    /**
107
     * Validation rules.
108
     *
109
     * @var string|\Closure
110
     */
111
    protected $rules = '';
112
113
    /**
114
     * @var callable
115
     */
116
    protected $validator;
117
118
    /**
119
     * Validation messages.
120
     *
121
     * @var array
122
     */
123
    protected $validationMessages = [];
124
125
    /**
126
     * Css required by this field.
127
     *
128
     * @var array
129
     */
130
    protected static $css = [];
131
132
    /**
133
     * Js required by this field.
134
     *
135
     * @var array
136
     */
137
    protected static $js = [];
138
139
    /**
140
     * Script for field.
141
     *
142
     * @var string
143
     */
144
    protected $script = '';
145
146
    /**
147
     * Element attributes.
148
     *
149
     * @var array
150
     */
151
    protected $attributes = [];
152
153
    /**
154
     * Parent form.
155
     *
156
     * @var Form
157
     */
158
    protected $form = null;
159
160
    /**
161
     * View for field to render.
162
     *
163
     * @var string
164
     */
165
    protected $view = '';
166
167
    /**
168
     * Help block.
169
     *
170
     * @var array
171
     */
172
    protected $help = [];
173
174
    /**
175
     * Key for errors.
176
     *
177
     * @var mixed
178
     */
179
    protected $errorKey;
180
181
    /**
182
     * Placeholder for this field.
183
     *
184
     * @var string|array
185
     */
186
    protected $placeholder;
187
188
    /**
189
     * Width for label and field.
190
     *
191
     * @var array
192
     */
193
    protected $width = [
194
        'label' => 2,
195
        'field' => 8,
196
    ];
197
198
    /**
199
     * If the form horizontal layout.
200
     *
201
     * @var bool
202
     */
203
    protected $horizontal = true;
204
205
    /**
206
     * column data format.
207
     *
208
     * @var \Closure
209
     */
210
    protected $customFormat = null;
211
212
    /**
213
     * @var bool
214
     */
215
    protected $display = true;
216
217
    /**
218
     * @var array
219
     */
220
    protected $labelClass = [];
221
222
    /**
223
     * @var array
224
     */
225
    protected $groupClass = [];
226
227
    /**
228
     * Field constructor.
229
     *
230
     * @param       $column
231
     * @param array $arguments
232
     */
233
    public function __construct($column, $arguments = [])
234
    {
235
        $this->column = $column;
236
        $this->label = $this->formatLabel($arguments);
237
        $this->id = $this->formatId($column);
238
    }
239
240
    /**
241
     * Get assets required by this field.
242
     *
243
     * @return array
244
     */
245
    public static function getAssets()
246
    {
247
        return [
248
            'css' => static::$css,
249
            'js'  => static::$js,
250
        ];
251
    }
252
253
    /**
254
     * Format the field element id.
255
     *
256
     * @param string|array $column
257
     *
258
     * @return string|array
259
     */
260
    protected function formatId($column)
261
    {
262
        return str_replace('.', '_', $column);
263
    }
264
265
    /**
266
     * Format the label value.
267
     *
268
     * @param array $arguments
269
     *
270
     * @return string
271
     */
272
    protected function formatLabel($arguments = [])
273
    {
274
        $column = is_array($this->column) ? current($this->column) : $this->column;
275
276
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($column);
277
278
        return str_replace(['.', '_'], ' ', $label);
279
    }
280
281
    /**
282
     * Format the name of the field.
283
     *
284
     * @param string $column
285
     *
286
     * @return array|mixed|string
287
     */
288
    protected function formatName($column)
289
    {
290
        if (is_string($column)) {
291
            $name = explode('.', $column);
292
293
            if (count($name) == 1) {
294
                return $name[0];
295
            }
296
297
            $html = array_shift($name);
298
            foreach ($name as $piece) {
299
                $html .= "[$piece]";
300
            }
301
302
            return $html;
303
        }
304
305
        if (is_array($this->column)) {
306
            $names = [];
307
            foreach ($this->column as $key => $name) {
308
                $names[$key] = $this->formatName($name);
309
            }
310
311
            return $names;
312
        }
313
314
        return '';
315
    }
316
317
    /**
318
     * Set form element name.
319
     *
320
     * @param string $name
321
     *
322
     * @return $this
323
     *
324
     * @author Edwin Hui
325
     */
326
    public function setElementName($name)
327
    {
328
        $this->elementName = $name;
329
330
        return $this;
331
    }
332
333
    /**
334
     * Fill data to the field.
335
     *
336
     * @param array $data
337
     *
338
     * @return void
339
     */
340
    public function fill($data)
341
    {
342
        // Field value is already setted.
343
//        if (!is_null($this->value)) {
344
//            return;
345
//        }
346
347
        $this->data = $data;
348
349
        if (is_array($this->column)) {
350
            foreach ($this->column as $key => $column) {
351
                $this->value[$key] = array_get($data, $column);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
352
            }
353
354
            return;
355
        }
356
357
        $this->value = array_get($data, $this->column);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
358
        if (isset($this->customFormat) && $this->customFormat instanceof \Closure) {
359
            $this->value = call_user_func($this->customFormat, $this->value);
360
        }
361
    }
362
363
    /**
364
     * custom format form column data when edit.
365
     *
366
     * @param \Closure $call
367
     *
368
     * @return $this
369
     */
370
    public function customFormat(\Closure $call)
371
    {
372
        $this->customFormat = $call;
373
374
        return $this;
375
    }
376
377
    /**
378
     * Set original value to the field.
379
     *
380
     * @param array $data
381
     *
382
     * @return void
383
     */
384
    public function setOriginal($data)
385
    {
386
        if (is_array($this->column)) {
387
            foreach ($this->column as $key => $column) {
388
                $this->original[$key] = array_get($data, $column);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
389
            }
390
391
            return;
392
        }
393
394
        $this->original = array_get($data, $this->column);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
395
    }
396
397
    /**
398
     * @param Form $form
399
     *
400
     * @return $this
401
     */
402
    public function setForm(Form $form = null)
403
    {
404
        $this->form = $form;
405
406
        return $this;
407
    }
408
409
    /**
410
     * Set width for field and label.
411
     *
412
     * @param int $field
413
     * @param int $label
414
     *
415
     * @return $this
416
     */
417
    public function setWidth($field = 8, $label = 2)
418
    {
419
        $this->width = [
420
            'label' => $label,
421
            'field' => $field,
422
        ];
423
424
        return $this;
425
    }
426
427
    /**
428
     * Set the field options.
429
     *
430
     * @param array $options
431
     *
432
     * @return $this
433
     */
434 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...
435
    {
436
        if ($options instanceof Arrayable) {
437
            $options = $options->toArray();
438
        }
439
440
        $this->options = array_merge($this->options, $options);
441
442
        return $this;
443
    }
444
445
    /**
446
     * Set the field option checked.
447
     *
448
     * @param array $checked
449
     *
450
     * @return $this
451
     */
452 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...
453
    {
454
        if ($checked instanceof Arrayable) {
455
            $checked = $checked->toArray();
456
        }
457
458
        $this->checked = array_merge($this->checked, $checked);
459
460
        return $this;
461
    }
462
463
    /**
464
     * Get or set rules.
465
     *
466
     * @param null  $rules
467
     * @param array $messages
468
     *
469
     * @return $this
470
     */
471
    public function rules($rules = null, $messages = [])
472
    {
473
        if ($rules instanceof \Closure) {
474
            $this->rules = $rules;
475
        }
476
477
        if (is_array($rules)) {
478
            $thisRuleArr = array_filter(explode('|', $this->rules));
479
480
            $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...
481
            if (in_array('required', $this->rules)) $this->required();
482
        } elseif (is_string($rules)) {
483
            $rules = array_filter(explode('|', "{$this->rules}|$rules"));
484
485
            if (in_array('required', $rules)) $this->required();
486
            $this->rules = implode('|', $rules);
487
        }
488
489
        $this->validationMessages = $messages;
490
491
        return $this;
492
    }
493
494
    /**
495
     * Get field validation rules.
496
     *
497
     * @return string
498
     */
499
    protected function getRules()
500
    {
501
        if ($this->rules instanceof \Closure) {
502
            return $this->rules->call($this, $this->form);
503
        }
504
505
        return $this->rules;
506
    }
507
508
    /**
509
     * Remove a specific rule by keyword.
510
     *
511
     * @param string $rule
512
     *
513
     * @return void
514
     */
515
    protected function removeRule($rule)
516
    {
517
        if (!is_string($this->rules)) {
518
            return;
519
        }
520
521
        $pattern = "/{$rule}[^\|]?(\||$)/";
522
        $this->rules = preg_replace($pattern, '', $this->rules, -1);
523
    }
524
525
    /**
526
     * Set field validator.
527
     *
528
     * @param callable $validator
529
     *
530
     * @return $this
531
     */
532
    public function validator(callable $validator)
533
    {
534
        $this->validator = $validator;
535
536
        return $this;
537
    }
538
539
    /**
540
     * Get key for error message.
541
     *
542
     * @return string
543
     */
544
    public function getErrorKey()
545
    {
546
        return $this->errorKey ?: $this->column;
547
    }
548
549
    /**
550
     * Set key for error message.
551
     *
552
     * @param string $key
553
     *
554
     * @return $this
555
     */
556
    public function setErrorKey($key)
557
    {
558
        $this->errorKey = $key;
559
560
        return $this;
561
    }
562
563
    /**
564
     * Set or get value of the field.
565
     *
566
     * @param null $value
567
     *
568
     * @return mixed
569
     */
570 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...
571
    {
572
        if (is_null($value)) {
573
            return is_null($this->value) ? $this->getDefault() : $this->value;
574
        }
575
576
        $this->value = $value;
577
578
        return $this;
579
    }
580
581
    /**
582
     * Set or get data.
583
     *
584
     * @param array $data
585
     *
586
     * @return $this
587
     */
588
    public function data(array $data = null)
589
    {
590
        if (is_null($data)) {
591
            return $this->data;
592
        }
593
594
        $this->data = $data;
595
596
        return $this;
597
    }
598
599
    /**
600
     * Set default value for field.
601
     *
602
     * @param $default
603
     *
604
     * @return $this
605
     */
606
    public function default($default)
607
    {
608
        $this->default = $default;
609
610
        return $this;
611
    }
612
613
    /**
614
     * Get default value.
615
     *
616
     * @return mixed
617
     */
618
    public function getDefault()
619
    {
620
        if ($this->default instanceof \Closure) {
621
            return call_user_func($this->default, $this->form);
622
        }
623
624
        return $this->default;
625
    }
626
627
    /**
628
     * Set help block for current field.
629
     *
630
     * @param string $text
631
     * @param string $icon
632
     *
633
     * @return $this
634
     */
635
    public function help($text = '', $icon = 'fa-info-circle')
636
    {
637
        $this->help = compact('text', 'icon');
638
639
        return $this;
640
    }
641
642
    /**
643
     * Get column of the field.
644
     *
645
     * @return string|array
646
     */
647
    public function column()
648
    {
649
        return $this->column;
650
    }
651
652
    /**
653
     * Get label of the field.
654
     *
655
     * @return string
656
     */
657
    public function label()
658
    {
659
        return $this->label;
660
    }
661
662
    /**
663
     * Get original value of the field.
664
     *
665
     * @return mixed
666
     */
667
    public function original()
668
    {
669
        return $this->original;
670
    }
671
672
    /**
673
     * Get validator for this field.
674
     *
675
     * @param array $input
676
     *
677
     * @return bool|Validator
678
     */
679
    public function getValidator(array $input)
680
    {
681
        if ($this->validator) {
682
            return $this->validator->call($this, $input);
683
        }
684
685
        $rules = $attributes = [];
686
687
        if (!$fieldRules = $this->getRules()) {
688
            return false;
689
        }
690
691
        if (is_string($this->column)) {
692
            if (!array_has($input, $this->column)) {
0 ignored issues
show
Deprecated Code introduced by
The function array_has() has been deprecated with message: Arr::has() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
693
                return false;
694
            }
695
696
            $input = $this->sanitizeInput($input, $this->column);
697
698
            $rules[$this->column] = $fieldRules;
699
            $attributes[$this->column] = $this->label;
700
        }
701
702
        if (is_array($this->column)) {
703
            foreach ($this->column as $key => $column) {
704
                if (!array_key_exists($column, $input)) {
705
                    continue;
706
                }
707
                $input[$column.$key] = array_get($input, $column);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
708
                $rules[$column.$key] = $fieldRules;
709
                $attributes[$column.$key] = $this->label."[$column]";
710
            }
711
        }
712
713
        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...
714
    }
715
716
    /**
717
     * Sanitize input data.
718
     *
719
     * @param array  $input
720
     * @param string $column
721
     *
722
     * @return array
723
     */
724
    protected function sanitizeInput($input, $column)
725
    {
726
        if ($this instanceof Field\MultipleSelect) {
727
            $value = array_get($input, $column);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
728
            array_set($input, $column, array_filter($value));
0 ignored issues
show
Deprecated Code introduced by
The function array_set() has been deprecated with message: Arr::set() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
729
        }
730
731
        return $input;
732
    }
733
734
    /**
735
     * Add html attributes to elements.
736
     *
737
     * @param array|string $attribute
738
     * @param mixed        $value
739
     *
740
     * @return $this
741
     */
742
    public function attribute($attribute, $value = null)
743
    {
744
        if (is_array($attribute)) {
745
            $this->attributes = array_merge($this->attributes, $attribute);
746
        } else {
747
            $this->attributes[$attribute] = (string) $value;
748
        }
749
750
        return $this;
751
    }
752
753
    /**
754
     * Specifies a regular expression against which to validate the value of the input.
755
     *
756
     * @param string $regexp
757
     *
758
     * @return Field
759
     */
760
    public function pattern($regexp)
761
    {
762
        return $this->attribute('pattern', $regexp);
763
    }
764
765
    /**
766
     * set the input filed required.
767
     *
768
     * @param bool $isLabelAsterisked
769
     *
770
     * @return Field
771
     */
772
    public function required($isLabelAsterisked = true)
773
    {
774
        if ($isLabelAsterisked) {
775
            $this->setLabelClass(['asterisk']);
776
        }
777
778
        return $this->attribute('required', true);
779
    }
780
781
    /**
782
     * Set the field automatically get focus.
783
     *
784
     * @return Field
785
     */
786
    public function autofocus()
787
    {
788
        return $this->attribute('autofocus', true);
789
    }
790
791
    /**
792
     * Set the field as readonly mode.
793
     *
794
     * @return Field
795
     */
796
    public function readOnly()
797
    {
798
        return $this->attribute('readonly', true);
799
    }
800
801
    /**
802
     * Set field as disabled.
803
     *
804
     * @return Field
805
     */
806
    public function disable()
807
    {
808
        return $this->attribute('disabled', true);
809
    }
810
811
    /**
812
     * Set field placeholder.
813
     *
814
     * @param string $placeholder
815
     *
816
     * @return Field
817
     */
818
    public function placeholder($placeholder = '')
819
    {
820
        $this->placeholder = $placeholder;
821
822
        return $this;
823
    }
824
825
    /**
826
     * Get placeholder.
827
     *
828
     * @return string
829
     */
830
    public function getPlaceholder()
831
    {
832
        return $this->placeholder ?: trans('admin.input').' '.$this->label;
833
    }
834
835
    /**
836
     * Prepare for a field value before update or insert.
837
     *
838
     * @param $value
839
     *
840
     * @return mixed
841
     */
842
    public function prepare($value)
843
    {
844
        return $value;
845
    }
846
847
    /**
848
     * Format the field attributes.
849
     *
850
     * @return string
851
     */
852
    protected function formatAttributes()
853
    {
854
        $html = [];
855
856
        foreach ($this->attributes as $name => $value) {
857
            $html[] = $name.'="'.e($value).'"';
858
        }
859
860
        return implode(' ', $html);
861
    }
862
863
    /**
864
     * @return $this
865
     */
866
    public function disableHorizontal()
867
    {
868
        $this->horizontal = false;
869
870
        return $this;
871
    }
872
873
    /**
874
     * @return array
875
     */
876
    public function getViewElementClasses()
877
    {
878
        if ($this->horizontal) {
879
            return [
880
                'label'      => "col-sm-{$this->width['label']} {$this->getLabelClass()}",
881
                'field'      => "col-sm-{$this->width['field']}",
882
                'form-group' => 'form-group ',
883
            ];
884
        }
885
886
        return ['label' => "{$this->getLabelClass()}", 'field' => '', 'form-group' => ''];
887
    }
888
889
    /**
890
     * Set form element class.
891
     *
892
     * @param string|array $class
893
     *
894
     * @return $this
895
     */
896
    public function setElementClass($class)
897
    {
898
        $this->elementClass = array_merge($this->elementClass, (array) $class);
899
900
        return $this;
901
    }
902
903
    /**
904
     * Get element class.
905
     *
906
     * @return array
907
     */
908
    protected function getElementClass()
909
    {
910
        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...
911
            $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...
912
913
            $this->elementClass = (array) str_replace(['[', ']'], '_', $name);
914
        }
915
916
        return $this->elementClass;
917
    }
918
919
    /**
920
     * Get element class string.
921
     *
922
     * @return mixed
923
     */
924 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...
925
    {
926
        $elementClass = $this->getElementClass();
927
928
        if (Arr::isAssoc($elementClass)) {
929
            $classes = [];
930
931
            foreach ($elementClass as $index => $class) {
932
                $classes[$index] = is_array($class) ? implode(' ', $class) : $class;
933
            }
934
935
            return $classes;
936
        }
937
938
        return implode(' ', $elementClass);
939
    }
940
941
    /**
942
     * Get element class selector.
943
     *
944
     * @return string|array
945
     */
946 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...
947
    {
948
        $elementClass = $this->getElementClass();
949
950
        if (Arr::isAssoc($elementClass)) {
951
            $classes = [];
952
953
            foreach ($elementClass as $index => $class) {
954
                $classes[$index] = '.'.(is_array($class) ? implode('.', $class) : $class);
955
            }
956
957
            return $classes;
958
        }
959
960
        return '.'.implode('.', $elementClass);
961
    }
962
963
    /**
964
     * Add the element class.
965
     *
966
     * @param $class
967
     *
968
     * @return $this
969
     */
970
    public function addElementClass($class)
971
    {
972
        if (is_array($class) || is_string($class)) {
973
            $this->elementClass = array_merge($this->elementClass, (array) $class);
974
975
            $this->elementClass = array_unique($this->elementClass);
976
        }
977
978
        return $this;
979
    }
980
981
    /**
982
     * Remove element class.
983
     *
984
     * @param $class
985
     *
986
     * @return $this
987
     */
988
    public function removeElementClass($class)
989
    {
990
        $delClass = [];
991
992
        if (is_string($class) || is_array($class)) {
993
            $delClass = (array) $class;
994
        }
995
996
        foreach ($delClass as $del) {
997
            if (($key = array_search($del, $this->elementClass)) !== false) {
998
                unset($this->elementClass[$key]);
999
            }
1000
        }
1001
1002
        return $this;
1003
    }
1004
1005
    /**
1006
     * Set form group class.
1007
     *
1008
     * @param string|array $class
1009
     *
1010
     * @return $this
1011
     */
1012
    public function setGroupClass($class)
1013
    : self
1014
    {
1015
        array_push($this->groupClass, $class);
1016
1017
        return $this;
1018
    }
1019
1020
    /**
1021
     * Get element class.
1022
     *
1023
     * @return array
1024
     */
1025
    protected function getGroupClass($default = false)
1026
    : string
1027
    {
1028
        return ($default ? 'form-group ' : '') . implode(' ', array_filter($this->groupClass));
1029
    }
1030
1031
    /**
1032
     * reset field className
1033
     *
1034
     * @param string $className
1035
     * @param string $resetClassName
1036
     *
1037
     * @return $this
1038
     */
1039
    public function resetElementClassName(string $className, string $resetClassName)
1040
    {
1041
        if (($key = array_search($className, $this->getElementClass())) !== false) {
1042
            $this->elementClass[$key] = $resetClassName;
1043
        }
1044
1045
        return $this;
1046
    }
1047
1048
    /**
1049
     * Add variables to field view.
1050
     *
1051
     * @param array $variables
1052
     *
1053
     * @return $this
1054
     */
1055
    protected function addVariables(array $variables = [])
1056
    {
1057
        $this->variables = array_merge($this->variables, $variables);
1058
1059
        return $this;
1060
    }
1061
1062
    /**
1063
     * @return string
1064
     */
1065
    public function getLabelClass()
1066
    : string
1067
    {
1068
        return implode(' ', $this->labelClass);
1069
    }
1070
1071
    /**
1072
     * @param array $labelClass
1073
     *
1074
     * @return self
1075
     */
1076
    public function setLabelClass(array $labelClass)
1077
    : self
1078
    {
1079
        $this->labelClass = $labelClass;
1080
1081
        return $this;
1082
    }
1083
1084
    /**
1085
     * Get the view variables of this field.
1086
     *
1087
     * @return array
1088
     */
1089
    public function variables()
1090
    {
1091
        return array_merge($this->variables, [
1092
            'id'          => $this->id,
1093
            '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...
1094
            'help'        => $this->help,
1095
            'class'       => $this->getElementClassString(),
1096
            'value'       => $this->value(),
1097
            'label'       => $this->label,
1098
            'viewClass'   => $this->getViewElementClasses(),
1099
            'column'      => $this->column,
1100
            'errorKey'    => $this->getErrorKey(),
1101
            'attributes'  => $this->formatAttributes(),
1102
            'placeholder' => $this->getPlaceholder(),
1103
        ]);
1104
    }
1105
1106
    /**
1107
     * Get view of this field.
1108
     *
1109
     * @return string
1110
     */
1111
    public function getView()
1112
    {
1113
        if (!empty($this->view)) {
1114
            return $this->view;
1115
        }
1116
1117
        $class = explode('\\', get_called_class());
1118
1119
        return 'admin::form.'.strtolower(end($class));
1120
    }
1121
1122
    /**
1123
     * Set view of current field.
1124
     *
1125
     * @return string
1126
     */
1127
    public function setView($view)
1128
    {
1129
        $this->view = $view;
1130
1131
        return $this;
1132
    }
1133
1134
    /**
1135
     * Get script of current field.
1136
     *
1137
     * @return string
1138
     */
1139
    public function getScript()
1140
    {
1141
        return $this->script;
1142
    }
1143
1144
    /**
1145
     * Set script of current field.
1146
     *
1147
     * @return self
1148
     */
1149
    public function setScript($script)
1150
    {
1151
        $this->script = $script;
1152
1153
        return $this;
1154
    }
1155
1156
    /**
1157
     * To set this field should render or not.
1158
     *
1159
     * @return self
1160
     */
1161
    public function setDisplay(bool $display)
1162
    {
1163
        $this->display = $display;
1164
1165
        return $this;
1166
    }
1167
1168
    /**
1169
     * If this field should render.
1170
     *
1171
     * @return bool
1172
     */
1173
    protected function shouldRender()
1174
    {
1175
        if (!$this->display) {
1176
            return false;
1177
        }
1178
1179
        return true;
1180
    }
1181
1182
    /**
1183
     * Render this filed.
1184
     *
1185
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1186
     */
1187
    public function render()
1188
    {
1189
        if (!$this->shouldRender()) {
1190
            return '';
1191
        }
1192
1193
        Admin::script($this->script);
1194
1195
        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 1195 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
1196
    }
1197
1198
    /**
1199
     * @return string
1200
     */
1201
    public function __toString()
1202
    {
1203
        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...
1204
    }
1205
}
1206