Completed
Push — master ( 2117a3...f9641d )
by Song
12s queued 10s
created

Field::formatId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Form;
4
5
use Closure;
6
use Encore\Admin\Admin;
7
use Encore\Admin\Form;
8
use Illuminate\Contracts\Support\Arrayable;
9
use Illuminate\Contracts\Support\Renderable;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Facades\Validator;
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 string|\Closure
112
     */
113
    protected $rules = '';
114
115
    /**
116
     * @var callable
117
     */
118
    protected $validator;
119
120
    /**
121
     * Validation messages.
122
     *
123
     * @var array
124
     */
125
    protected $validationMessages = [];
126
127
    /**
128
     * Css required by this field.
129
     *
130
     * @var array
131
     */
132
    protected static $css = [];
133
134
    /**
135
     * Js required by this field.
136
     *
137
     * @var array
138
     */
139
    protected static $js = [];
140
141
    /**
142
     * Script for field.
143
     *
144
     * @var string
145
     */
146
    protected $script = '';
147
148
    /**
149
     * Element attributes.
150
     *
151
     * @var array
152
     */
153
    protected $attributes = [];
154
155
    /**
156
     * Parent form.
157
     *
158
     * @var Form
159
     */
160
    protected $form = null;
161
162
    /**
163
     * View for field to render.
164
     *
165
     * @var string
166
     */
167
    protected $view = '';
168
169
    /**
170
     * Help block.
171
     *
172
     * @var array
173
     */
174
    protected $help = [];
175
176
    /**
177
     * Key for errors.
178
     *
179
     * @var mixed
180
     */
181
    protected $errorKey;
182
183
    /**
184
     * Placeholder for this field.
185
     *
186
     * @var string|array
187
     */
188
    protected $placeholder;
189
190
    /**
191
     * Width for label and field.
192
     *
193
     * @var array
194
     */
195
    protected $width = [
196
        'label' => 2,
197
        'field' => 8,
198
    ];
199
200
    /**
201
     * If the form horizontal layout.
202
     *
203
     * @var bool
204
     */
205
    protected $horizontal = true;
206
207
    /**
208
     * column data format.
209
     *
210
     * @var \Closure
211
     */
212
    protected $customFormat = null;
213
214
    /**
215
     * @var bool
216
     */
217
    protected $display = true;
218
219
    /**
220
     * @var array
221
     */
222
    protected $labelClass = [];
223
224
    /**
225
     * @var array
226
     */
227
    protected $groupClass = [];
228
229
    /**
230
     * @var \Closure
231
     */
232
    protected $callback;
233
234
    /**
235
     * Field constructor.
236
     *
237
     * @param       $column
238
     * @param array $arguments
239
     */
240
    public function __construct($column, $arguments = [])
241
    {
242
        $this->column = $column;
243
        $this->label = $this->formatLabel($arguments);
244
        $this->id = $this->formatId($column);
245
    }
246
247
    /**
248
     * Get assets required by this field.
249
     *
250
     * @return array
251
     */
252
    public static function getAssets()
253
    {
254
        return [
255
            'css' => static::$css,
256
            'js'  => static::$js,
257
        ];
258
    }
259
260
    /**
261
     * Format the field element id.
262
     *
263
     * @param string|array $column
264
     *
265
     * @return string|array
266
     */
267
    protected function formatId($column)
268
    {
269
        return str_replace('.', '_', $column);
270
    }
271
272
    /**
273
     * Format the label value.
274
     *
275
     * @param array $arguments
276
     *
277
     * @return string
278
     */
279
    protected function formatLabel($arguments = [])
280
    {
281
        $column = is_array($this->column) ? current($this->column) : $this->column;
282
283
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($column);
284
285
        return str_replace(['.', '_'], ' ', $label);
286
    }
287
288
    /**
289
     * Format the name of the field.
290
     *
291
     * @param string $column
292
     *
293
     * @return array|mixed|string
294
     */
295
    protected function formatName($column)
296
    {
297
        if (is_string($column)) {
298
            $name = explode('.', $column);
299
300
            if (count($name) == 1) {
301
                return $name[0];
302
            }
303
304
            $html = array_shift($name);
305
            foreach ($name as $piece) {
306
                $html .= "[$piece]";
307
            }
308
309
            return $html;
310
        }
311
312
        if (is_array($this->column)) {
313
            $names = [];
314
            foreach ($this->column as $key => $name) {
315
                $names[$key] = $this->formatName($name);
316
            }
317
318
            return $names;
319
        }
320
321
        return '';
322
    }
323
324
    /**
325
     * Set form element name.
326
     *
327
     * @param string $name
328
     *
329
     * @return $this
330
     *
331
     * @author Edwin Hui
332
     */
333
    public function setElementName($name)
334
    {
335
        $this->elementName = $name;
336
337
        return $this;
338
    }
339
340
    /**
341
     * Fill data to the field.
342
     *
343
     * @param array $data
344
     *
345
     * @return void
346
     */
347
    public function fill($data)
348
    {
349
        // Field value is already setted.
350
//        if (!is_null($this->value)) {
351
//            return;
352
//        }
353
354
        $this->data = $data;
355
356
        if (is_array($this->column)) {
357
            foreach ($this->column as $key => $column) {
358
                $this->value[$key] = Arr::get($data, $column);
359
            }
360
361
            return;
362
        }
363
364
        $this->value = Arr::get($data, $this->column);
365
        if (isset($this->customFormat) && $this->customFormat instanceof \Closure) {
366
            $this->value = call_user_func($this->customFormat, $this->value);
367
        }
368
    }
369
370
    /**
371
     * custom format form column data when edit.
372
     *
373
     * @param \Closure $call
374
     *
375
     * @return $this
376
     */
377
    public function customFormat(\Closure $call)
378
    {
379
        $this->customFormat = $call;
380
381
        return $this;
382
    }
383
384
    /**
385
     * Set original value to the field.
386
     *
387
     * @param array $data
388
     *
389
     * @return void
390
     */
391
    public function setOriginal($data)
392
    {
393
        if (is_array($this->column)) {
394
            foreach ($this->column as $key => $column) {
395
                $this->original[$key] = Arr::get($data, $column);
396
            }
397
398
            return;
399
        }
400
401
        $this->original = Arr::get($data, $this->column);
402
    }
403
404
    /**
405
     * @param Form $form
406
     *
407
     * @return $this
408
     */
409
    public function setForm(Form $form = null)
410
    {
411
        $this->form = $form;
412
413
        return $this;
414
    }
415
416
    /**
417
     * Set width for field and label.
418
     *
419
     * @param int $field
420
     * @param int $label
421
     *
422
     * @return $this
423
     */
424
    public function setWidth($field = 8, $label = 2)
425
    {
426
        $this->width = [
427
            'label' => $label,
428
            'field' => $field,
429
        ];
430
431
        return $this;
432
    }
433
434
    /**
435
     * Set the field options.
436
     *
437
     * @param array $options
438
     *
439
     * @return $this
440
     */
441 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...
442
    {
443
        if ($options instanceof Arrayable) {
444
            $options = $options->toArray();
445
        }
446
447
        $this->options = array_merge($this->options, $options);
448
449
        return $this;
450
    }
451
452
    /**
453
     * Set the field option checked.
454
     *
455
     * @param array $checked
456
     *
457
     * @return $this
458
     */
459 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...
460
    {
461
        if ($checked instanceof Arrayable) {
462
            $checked = $checked->toArray();
463
        }
464
465
        $this->checked = array_merge($this->checked, $checked);
466
467
        return $this;
468
    }
469
470
    /**
471
     * Add `required` attribute to current field if has required rule,
472
     * except file and image fields.
473
     *
474
     * @param array $rules
475
     */
476
    protected function addRequiredAttribute($rules)
477
    {
478
        if (!in_array('required', $rules)) {
479
            return;
480
        }
481
482
        if ($this instanceof Form\Field\MultipleFile
483
            || $this instanceof Form\Field\File) {
484
            return;
485
        }
486
487
        $this->required();
488
    }
489
490
    /**
491
     * Get or set rules.
492
     *
493
     * @param null  $rules
494
     * @param array $messages
495
     *
496
     * @return $this
497
     */
498
    public function rules($rules = null, $messages = [])
499
    {
500
        if ($rules instanceof \Closure) {
501
            $this->rules = $rules;
502
        }
503
504
        if (is_array($rules)) {
505
            $thisRuleArr = array_filter(explode('|', $this->rules));
506
507
            $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...
508
509
            $this->addRequiredAttribute($this->rules);
510
        } elseif (is_string($rules)) {
511
            $rules = array_filter(explode('|', "{$this->rules}|$rules"));
512
513
            $this->addRequiredAttribute($rules);
514
515
            $this->rules = implode('|', $rules);
516
        }
517
518
        $this->validationMessages = $messages;
519
520
        return $this;
521
    }
522
523
    /**
524
     * Get field validation rules.
525
     *
526
     * @return string
527
     */
528
    protected function getRules()
529
    {
530
        if ($this->rules instanceof \Closure) {
531
            return $this->rules->call($this, $this->form);
532
        }
533
534
        return $this->rules;
535
    }
536
537
    /**
538
     * Remove a specific rule by keyword.
539
     *
540
     * @param string $rule
541
     *
542
     * @return void
543
     */
544
    protected function removeRule($rule)
545
    {
546
        if (!is_string($this->rules)) {
547
            return;
548
        }
549
550
        $pattern = "/{$rule}[^\|]?(\||$)/";
551
        $this->rules = preg_replace($pattern, '', $this->rules, -1);
552
    }
553
554
    /**
555
     * Set field validator.
556
     *
557
     * @param callable $validator
558
     *
559
     * @return $this
560
     */
561
    public function validator(callable $validator)
562
    {
563
        $this->validator = $validator;
564
565
        return $this;
566
    }
567
568
    /**
569
     * Get key for error message.
570
     *
571
     * @return string
572
     */
573
    public function getErrorKey()
574
    {
575
        return $this->errorKey ?: $this->column;
576
    }
577
578
    /**
579
     * Set key for error message.
580
     *
581
     * @param string $key
582
     *
583
     * @return $this
584
     */
585
    public function setErrorKey($key)
586
    {
587
        $this->errorKey = $key;
588
589
        return $this;
590
    }
591
592
    /**
593
     * Set or get value of the field.
594
     *
595
     * @param null $value
596
     *
597
     * @return mixed
598
     */
599 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...
600
    {
601
        if (is_null($value)) {
602
            return is_null($this->value) ? $this->getDefault() : $this->value;
603
        }
604
605
        $this->value = $value;
606
607
        return $this;
608
    }
609
610
    /**
611
     * Set or get data.
612
     *
613
     * @param array $data
614
     *
615
     * @return $this
616
     */
617
    public function data(array $data = null)
618
    {
619
        if (is_null($data)) {
620
            return $this->data;
621
        }
622
623
        $this->data = $data;
624
625
        return $this;
626
    }
627
628
    /**
629
     * Set default value for field.
630
     *
631
     * @param $default
632
     *
633
     * @return $this
634
     */
635
    public function default($default)
636
    {
637
        $this->default = $default;
638
639
        return $this;
640
    }
641
642
    /**
643
     * Get default value.
644
     *
645
     * @return mixed
646
     */
647
    public function getDefault()
648
    {
649
        if ($this->default instanceof \Closure) {
650
            return call_user_func($this->default, $this->form);
651
        }
652
653
        return $this->default;
654
    }
655
656
    /**
657
     * Set help block for current field.
658
     *
659
     * @param string $text
660
     * @param string $icon
661
     *
662
     * @return $this
663
     */
664
    public function help($text = '', $icon = 'fa-info-circle')
665
    {
666
        $this->help = compact('text', 'icon');
667
668
        return $this;
669
    }
670
671
    /**
672
     * Get column of the field.
673
     *
674
     * @return string|array
675
     */
676
    public function column()
677
    {
678
        return $this->column;
679
    }
680
681
    /**
682
     * Get label of the field.
683
     *
684
     * @return string
685
     */
686
    public function label()
687
    {
688
        return $this->label;
689
    }
690
691
    /**
692
     * Get original value of the field.
693
     *
694
     * @return mixed
695
     */
696
    public function original()
697
    {
698
        return $this->original;
699
    }
700
701
    /**
702
     * Get validator for this field.
703
     *
704
     * @param array $input
705
     *
706
     * @return bool|Validator
707
     */
708
    public function getValidator(array $input)
709
    {
710
        if ($this->validator) {
711
            return $this->validator->call($this, $input);
712
        }
713
714
        $rules = $attributes = [];
715
716
        if (!$fieldRules = $this->getRules()) {
717
            return false;
718
        }
719
720
        if (is_string($this->column)) {
721
            if (!Arr::has($input, $this->column)) {
722
                return false;
723
            }
724
725
            $input = $this->sanitizeInput($input, $this->column);
726
727
            $rules[$this->column] = $fieldRules;
728
            $attributes[$this->column] = $this->label;
729
        }
730
731
        if (is_array($this->column)) {
732
            foreach ($this->column as $key => $column) {
733
                if (!array_key_exists($column, $input)) {
734
                    continue;
735
                }
736
                $input[$column.$key] = Arr::get($input, $column);
737
                $rules[$column.$key] = $fieldRules;
738
                $attributes[$column.$key] = $this->label."[$column]";
739
            }
740
        }
741
742
        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...
743
    }
744
745
    /**
746
     * Sanitize input data.
747
     *
748
     * @param array  $input
749
     * @param string $column
750
     *
751
     * @return array
752
     */
753
    protected function sanitizeInput($input, $column)
754
    {
755
        if ($this instanceof Field\MultipleSelect) {
756
            $value = Arr::get($input, $column);
757
            Arr::set($input, $column, array_filter($value));
758
        }
759
760
        return $input;
761
    }
762
763
    /**
764
     * Add html attributes to elements.
765
     *
766
     * @param array|string $attribute
767
     * @param mixed        $value
768
     *
769
     * @return $this
770
     */
771
    public function attribute($attribute, $value = null)
772
    {
773
        if (is_array($attribute)) {
774
            $this->attributes = array_merge($this->attributes, $attribute);
775
        } else {
776
            $this->attributes[$attribute] = (string) $value;
777
        }
778
779
        return $this;
780
    }
781
782
    /**
783
     * Specifies a regular expression against which to validate the value of the input.
784
     *
785
     * @param string $regexp
786
     *
787
     * @return Field
788
     */
789
    public function pattern($regexp)
790
    {
791
        return $this->attribute('pattern', $regexp);
792
    }
793
794
    /**
795
     * set the input filed required.
796
     *
797
     * @param bool $isLabelAsterisked
798
     *
799
     * @return Field
800
     */
801
    public function required($isLabelAsterisked = true)
802
    {
803
        if ($isLabelAsterisked) {
804
            $this->setLabelClass(['asterisk']);
805
        }
806
807
        return $this->attribute('required', true);
808
    }
809
810
    /**
811
     * Set the field automatically get focus.
812
     *
813
     * @return Field
814
     */
815
    public function autofocus()
816
    {
817
        return $this->attribute('autofocus', true);
818
    }
819
820
    /**
821
     * Set the field as readonly mode.
822
     *
823
     * @return Field
824
     */
825
    public function readOnly()
826
    {
827
        return $this->attribute('readonly', true);
828
    }
829
830
    /**
831
     * Set field as disabled.
832
     *
833
     * @return Field
834
     */
835
    public function disable()
836
    {
837
        return $this->attribute('disabled', true);
838
    }
839
840
    /**
841
     * Set field placeholder.
842
     *
843
     * @param string $placeholder
844
     *
845
     * @return Field
846
     */
847
    public function placeholder($placeholder = '')
848
    {
849
        $this->placeholder = $placeholder;
850
851
        return $this;
852
    }
853
854
    /**
855
     * Get placeholder.
856
     *
857
     * @return string
858
     */
859
    public function getPlaceholder()
860
    {
861
        return $this->placeholder ?: trans('admin.input').' '.$this->label;
862
    }
863
864
    /**
865
     * Prepare for a field value before update or insert.
866
     *
867
     * @param $value
868
     *
869
     * @return mixed
870
     */
871
    public function prepare($value)
872
    {
873
        return $value;
874
    }
875
876
    /**
877
     * Format the field attributes.
878
     *
879
     * @return string
880
     */
881
    protected function formatAttributes()
882
    {
883
        $html = [];
884
885
        foreach ($this->attributes as $name => $value) {
886
            $html[] = $name.'="'.e($value).'"';
887
        }
888
889
        return implode(' ', $html);
890
    }
891
892
    /**
893
     * @return $this
894
     */
895
    public function disableHorizontal()
896
    {
897
        $this->horizontal = false;
898
899
        return $this;
900
    }
901
902
    /**
903
     * @return array
904
     */
905
    public function getViewElementClasses()
906
    {
907
        if ($this->horizontal) {
908
            return [
909
                'label'      => "col-sm-{$this->width['label']} {$this->getLabelClass()}",
910
                'field'      => "col-sm-{$this->width['field']}",
911
                'form-group' => 'form-group ',
912
            ];
913
        }
914
915
        return ['label' => "{$this->getLabelClass()}", 'field' => '', 'form-group' => ''];
916
    }
917
918
    /**
919
     * Set form element class.
920
     *
921
     * @param string|array $class
922
     *
923
     * @return $this
924
     */
925
    public function setElementClass($class)
926
    {
927
        $this->elementClass = array_merge($this->elementClass, (array) $class);
928
929
        return $this;
930
    }
931
932
    /**
933
     * Get element class.
934
     *
935
     * @return array
936
     */
937
    protected function getElementClass()
938
    {
939
        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...
940
            $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...
941
942
            $this->elementClass = (array) str_replace(['[', ']'], '_', $name);
943
        }
944
945
        return $this->elementClass;
946
    }
947
948
    /**
949
     * Get element class string.
950
     *
951
     * @return mixed
952
     */
953 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...
954
    {
955
        $elementClass = $this->getElementClass();
956
957
        if (Arr::isAssoc($elementClass)) {
958
            $classes = [];
959
960
            foreach ($elementClass as $index => $class) {
961
                $classes[$index] = is_array($class) ? implode(' ', $class) : $class;
962
            }
963
964
            return $classes;
965
        }
966
967
        return implode(' ', $elementClass);
968
    }
969
970
    /**
971
     * Get element class selector.
972
     *
973
     * @return string|array
974
     */
975 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...
976
    {
977
        $elementClass = $this->getElementClass();
978
979
        if (Arr::isAssoc($elementClass)) {
980
            $classes = [];
981
982
            foreach ($elementClass as $index => $class) {
983
                $classes[$index] = '.'.(is_array($class) ? implode('.', $class) : $class);
984
            }
985
986
            return $classes;
987
        }
988
989
        return '.'.implode('.', $elementClass);
990
    }
991
992
    /**
993
     * Add the element class.
994
     *
995
     * @param $class
996
     *
997
     * @return $this
998
     */
999
    public function addElementClass($class)
1000
    {
1001
        if (is_array($class) || is_string($class)) {
1002
            $this->elementClass = array_merge($this->elementClass, (array) $class);
1003
1004
            $this->elementClass = array_unique($this->elementClass);
1005
        }
1006
1007
        return $this;
1008
    }
1009
1010
    /**
1011
     * Remove element class.
1012
     *
1013
     * @param $class
1014
     *
1015
     * @return $this
1016
     */
1017
    public function removeElementClass($class)
1018
    {
1019
        $delClass = [];
1020
1021
        if (is_string($class) || is_array($class)) {
1022
            $delClass = (array) $class;
1023
        }
1024
1025
        foreach ($delClass as $del) {
1026
            if (($key = array_search($del, $this->elementClass)) !== false) {
1027
                unset($this->elementClass[$key]);
1028
            }
1029
        }
1030
1031
        return $this;
1032
    }
1033
1034
    /**
1035
     * Set form group class.
1036
     *
1037
     * @param string|array $class
1038
     *
1039
     * @return $this
1040
     */
1041
    public function setGroupClass($class)
1042
    : self
1043
    {
1044
        array_push($this->groupClass, $class);
1045
1046
        return $this;
1047
    }
1048
1049
    /**
1050
     * Get element class.
1051
     *
1052
     * @return array
1053
     */
1054
    protected function getGroupClass($default = false)
1055
    : string
1056
    {
1057
        return ($default ? 'form-group ' : '').implode(' ', array_filter($this->groupClass));
1058
    }
1059
1060
    /**
1061
     * reset field className.
1062
     *
1063
     * @param string $className
1064
     * @param string $resetClassName
1065
     *
1066
     * @return $this
1067
     */
1068
    public function resetElementClassName(string $className, string $resetClassName)
1069
    {
1070
        if (($key = array_search($className, $this->getElementClass())) !== false) {
1071
            $this->elementClass[$key] = $resetClassName;
1072
        }
1073
1074
        return $this;
1075
    }
1076
1077
    /**
1078
     * Add variables to field view.
1079
     *
1080
     * @param array $variables
1081
     *
1082
     * @return $this
1083
     */
1084
    protected function addVariables(array $variables = [])
1085
    {
1086
        $this->variables = array_merge($this->variables, $variables);
1087
1088
        return $this;
1089
    }
1090
1091
    /**
1092
     * @return string
1093
     */
1094
    public function getLabelClass()
1095
    : string
1096
    {
1097
        return implode(' ', $this->labelClass);
1098
    }
1099
1100
    /**
1101
     * @param array $labelClass
1102
     *
1103
     * @return self
1104
     */
1105
    public function setLabelClass(array $labelClass)
1106
    : self
1107
    {
1108
        $this->labelClass = $labelClass;
1109
1110
        return $this;
1111
    }
1112
1113
    /**
1114
     * Get the view variables of this field.
1115
     *
1116
     * @return array
1117
     */
1118
    public function variables()
1119
    {
1120
        return array_merge($this->variables, [
1121
            'id'          => $this->id,
1122
            '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...
1123
            'help'        => $this->help,
1124
            'class'       => $this->getElementClassString(),
1125
            'value'       => $this->value(),
1126
            'label'       => $this->label,
1127
            'viewClass'   => $this->getViewElementClasses(),
1128
            'column'      => $this->column,
1129
            'errorKey'    => $this->getErrorKey(),
1130
            'attributes'  => $this->formatAttributes(),
1131
            'placeholder' => $this->getPlaceholder(),
1132
        ]);
1133
    }
1134
1135
    /**
1136
     * Get view of this field.
1137
     *
1138
     * @return string
1139
     */
1140
    public function getView()
1141
    {
1142
        if (!empty($this->view)) {
1143
            return $this->view;
1144
        }
1145
1146
        $class = explode('\\', get_called_class());
1147
1148
        return 'admin::form.'.strtolower(end($class));
1149
    }
1150
1151
    /**
1152
     * Set view of current field.
1153
     *
1154
     * @return string
1155
     */
1156
    public function setView($view)
1157
    {
1158
        $this->view = $view;
1159
1160
        return $this;
1161
    }
1162
1163
    /**
1164
     * Get script of current field.
1165
     *
1166
     * @return string
1167
     */
1168
    public function getScript()
1169
    {
1170
        return $this->script;
1171
    }
1172
1173
    /**
1174
     * Set script of current field.
1175
     *
1176
     * @return self
1177
     */
1178
    public function setScript($script)
1179
    {
1180
        $this->script = $script;
1181
1182
        return $this;
1183
    }
1184
1185
    /**
1186
     * To set this field should render or not.
1187
     *
1188
     * @return self
1189
     */
1190
    public function setDisplay(bool $display)
1191
    {
1192
        $this->display = $display;
1193
1194
        return $this;
1195
    }
1196
1197
    /**
1198
     * If this field should render.
1199
     *
1200
     * @return bool
1201
     */
1202
    protected function shouldRender()
1203
    {
1204
        if (!$this->display) {
1205
            return false;
1206
        }
1207
1208
        return true;
1209
    }
1210
1211
    /**
1212
     * @param \Closure $callback
1213
     *
1214
     * @return \Encore\Admin\Form\Field
1215
     */
1216
    public function with(Closure $callback)
1217
    {
1218
        $this->callback = $callback;
1219
1220
        return $this;
1221
    }
1222
1223
    /**
1224
     * Render this filed.
1225
     *
1226
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1227
     */
1228
    public function render()
1229
    {
1230
        if (!$this->shouldRender()) {
1231
            return '';
1232
        }
1233
1234
        if ($this->callback instanceof Closure) {
1235
            $this->value = $this->callback->call($this->form->model(), $this->value, $this);
1236
        }
1237
1238
        Admin::script($this->script);
1239
1240
        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 1240 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
1241
    }
1242
1243
    /**
1244
     * @return string
1245
     */
1246
    public function __toString()
1247
    {
1248
        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...
1249
    }
1250
}
1251