Completed
Push — master ( 82be35...1537c0 )
by Song
02:28
created

src/Form/Field.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Encore\Admin\Form;
4
5
use Closure;
6
use Encore\Admin\Admin;
7
use Encore\Admin\Form;
8
use Encore\Admin\Widgets\Form as WidgetForm;
9
use Illuminate\Contracts\Support\Arrayable;
10
use Illuminate\Contracts\Support\Renderable;
11
use Illuminate\Support\Arr;
12
use Illuminate\Support\Str;
13
use Illuminate\Support\Traits\Macroable;
14
15
/**
16
 * Class Field.
17
 */
18
class Field implements Renderable
19
{
20
    use Macroable;
21
22
    const FILE_DELETE_FLAG = '_file_del_';
23
    const FILE_SORT_FLAG = '_file_sort_';
24
25
    /**
26
     * Element id.
27
     *
28
     * @var array|string
29
     */
30
    protected $id;
31
32
    /**
33
     * Element value.
34
     *
35
     * @var mixed
36
     */
37
    protected $value;
38
39
    /**
40
     * Data of all original columns of value.
41
     *
42
     * @var mixed
43
     */
44
    protected $data;
45
46
    /**
47
     * Field original value.
48
     *
49
     * @var mixed
50
     */
51
    protected $original;
52
53
    /**
54
     * Field default value.
55
     *
56
     * @var mixed
57
     */
58
    protected $default;
59
60
    /**
61
     * Element label.
62
     *
63
     * @var string
64
     */
65
    protected $label = '';
66
67
    /**
68
     * Column name.
69
     *
70
     * @var string|array
71
     */
72
    protected $column = '';
73
74
    /**
75
     * Form element name.
76
     *
77
     * @var string
78
     */
79
    protected $elementName = [];
80
81
    /**
82
     * Form element classes.
83
     *
84
     * @var array
85
     */
86
    protected $elementClass = [];
87
88
    /**
89
     * Variables of elements.
90
     *
91
     * @var array
92
     */
93
    protected $variables = [];
94
95
    /**
96
     * Options for specify elements.
97
     *
98
     * @var array
99
     */
100
    protected $options = [];
101
102
    /**
103
     * Checked for specify elements.
104
     *
105
     * @var array
106
     */
107
    protected $checked = [];
108
109
    /**
110
     * Validation rules.
111
     *
112
     * @var array|\Closure
113
     */
114
    protected $rules = [];
115
116
    /**
117
     * The validation rules for creation.
118
     *
119
     * @var array|\Closure
120
     */
121
    public $creationRules = [];
122
123
    /**
124
     * The validation rules for updates.
125
     *
126
     * @var array|\Closure
127
     */
128
    public $updateRules = [];
129
130
    /**
131
     * @var \Closure
132
     */
133
    protected $validator;
134
135
    /**
136
     * Validation messages.
137
     *
138
     * @var array
139
     */
140
    protected $validationMessages = [];
141
142
    /**
143
     * Css required by this field.
144
     *
145
     * @var array
146
     */
147
    protected static $css = [];
148
149
    /**
150
     * Js required by this field.
151
     *
152
     * @var array
153
     */
154
    protected static $js = [];
155
156
    /**
157
     * Script for field.
158
     *
159
     * @var string
160
     */
161
    protected $script = '';
162
163
    /**
164
     * Element attributes.
165
     *
166
     * @var array
167
     */
168
    protected $attributes = [];
169
170
    /**
171
     * Parent form.
172
     *
173
     * @var Form
174
     */
175
    protected $form = null;
176
177
    /**
178
     * View for field to render.
179
     *
180
     * @var string
181
     */
182
    protected $view = '';
183
184
    /**
185
     * Help block.
186
     *
187
     * @var array
188
     */
189
    protected $help = [];
190
191
    /**
192
     * Key for errors.
193
     *
194
     * @var mixed
195
     */
196
    protected $errorKey;
197
198
    /**
199
     * Placeholder for this field.
200
     *
201
     * @var string|array
202
     */
203
    protected $placeholder;
204
205
    /**
206
     * Width for label and field.
207
     *
208
     * @var array
209
     */
210
    protected $width = [
211
        'label' => 2,
212
        'field' => 8,
213
    ];
214
215
    /**
216
     * If the form horizontal layout.
217
     *
218
     * @var bool
219
     */
220
    protected $horizontal = true;
221
222
    /**
223
     * column data format.
224
     *
225
     * @var \Closure
226
     */
227
    protected $customFormat = null;
228
229
    /**
230
     * @var bool
231
     */
232
    protected $display = true;
233
234
    /**
235
     * @var array
236
     */
237
    protected $labelClass = [];
238
239
    /**
240
     * @var array
241
     */
242
    protected $groupClass = [];
243
244
    /**
245
     * @var \Closure
246
     */
247
    protected $callback;
248
249
    /**
250
     * @var bool
251
     */
252
    public $isJsonType = false;
253
254
    /**
255
     * Field constructor.
256
     *
257
     * @param       $column
258
     * @param array $arguments
259
     */
260
    public function __construct($column = '', $arguments = [])
261
    {
262
        $this->column = $this->formatColumn($column);
263
        $this->label = $this->formatLabel($arguments);
264
        $this->id = $this->formatId($column);
265
    }
266
267
    /**
268
     * Get assets required by this field.
269
     *
270
     * @return array
271
     */
272
    public static function getAssets()
273
    {
274
        return [
275
            'css' => static::$css,
276
            'js'  => static::$js,
277
        ];
278
    }
279
280
    /**
281
     * Format the field column name.
282
     *
283
     * @param string $column
284
     *
285
     * @return mixed|string
286
     */
287
    protected function formatColumn($column = '')
288
    {
289 View Code Duplication
        if (Str::contains($column, '->')) {
290
            $this->isJsonType = true;
291
292
            $column = str_replace('->', '.', $column);
293
        }
294
295
        return $column;
296
    }
297
298
    /**
299
     * Format the field element id.
300
     *
301
     * @param string|array $column
302
     *
303
     * @return string|array
304
     */
305
    protected function formatId($column)
306
    {
307
        return str_replace('.', '_', $column);
308
    }
309
310
    /**
311
     * Format the label value.
312
     *
313
     * @param array $arguments
314
     *
315
     * @return string
316
     */
317
    protected function formatLabel($arguments = []): string
318
    {
319
        $column = is_array($this->column) ? current($this->column) : $this->column;
320
321
        $label = $arguments[0] ?? ucfirst($column);
322
323
        return str_replace(['.', '_', '->'], ' ', $label);
324
    }
325
326
    /**
327
     * Format the name of the field.
328
     *
329
     * @param string $column
330
     *
331
     * @return array|mixed|string
332
     */
333
    protected function formatName($column)
334
    {
335
        if (is_string($column)) {
336 View Code Duplication
            if (Str::contains($column, '->')) {
337
                $name = explode('->', $column);
338
            } else {
339
                $name = explode('.', $column);
340
            }
341
342
            if (count($name) === 1) {
343
                return $name[0];
344
            }
345
346
            $html = array_shift($name);
347
            foreach ($name as $piece) {
348
                $html .= "[$piece]";
349
            }
350
351
            return $html;
352
        }
353
354
        if (is_array($this->column)) {
355
            $names = [];
356
            foreach ($this->column as $key => $name) {
357
                $names[$key] = $this->formatName($name);
358
            }
359
360
            return $names;
361
        }
362
363
        return '';
364
    }
365
366
    /**
367
     * Set form element name.
368
     *
369
     * @param string $name
370
     *
371
     * @return $this
372
     *
373
     * @author Edwin Hui
374
     */
375
    public function setElementName($name): self
376
    {
377
        $this->elementName = $name;
378
379
        return $this;
380
    }
381
382
    /**
383
     * Fill data to the field.
384
     *
385
     * @param array $data
386
     *
387
     * @return void
388
     */
389 View Code Duplication
    public function fill($data)
390
    {
391
        $this->data = $data;
392
393
        if (is_array($this->column)) {
394
            foreach ($this->column as $key => $column) {
395
                $this->value[$key] = Arr::get($data, $column);
396
            }
397
398
            return;
399
        }
400
401
        $this->value = Arr::get($data, $this->column);
402
403
        $this->formatValue();
404
    }
405
406
    /**
407
     * Format value by passing custom formater.
408
     */
409
    protected function formatValue()
410
    {
411
        if (isset($this->customFormat) && $this->customFormat instanceof \Closure) {
412
            $this->value = call_user_func($this->customFormat, $this->value);
413
        }
414
    }
415
416
    /**
417
     * custom format form column data when edit.
418
     *
419
     * @param \Closure $call
420
     *
421
     * @return $this
422
     */
423
    public function customFormat(\Closure $call): self
424
    {
425
        $this->customFormat = $call;
426
427
        return $this;
428
    }
429
430
    /**
431
     * Set original value to the field.
432
     *
433
     * @param array $data
434
     *
435
     * @return void
436
     */
437 View Code Duplication
    public function setOriginal($data)
438
    {
439
        if (is_array($this->column)) {
440
            foreach ($this->column as $key => $column) {
441
                $this->original[$key] = Arr::get($data, $column);
442
            }
443
444
            return;
445
        }
446
447
        $this->original = Arr::get($data, $this->column);
448
    }
449
450
    /**
451
     * @param Form $form
452
     *
453
     * @return $this
454
     */
455
    public function setForm(Form $form = null)
456
    {
457
        $this->form = $form;
458
459
        return $this;
460
    }
461
462
    /**
463
     * Set Widget/Form as field parent.
464
     *
465
     * @param WidgetForm $form
466
     *
467
     * @return $this
468
     */
469
    public function setWidgetForm(WidgetForm $form)
470
    {
471
        $this->form = $form;
472
473
        return $this;
474
    }
475
476
    /**
477
     * Set width for field and label.
478
     *
479
     * @param int $field
480
     * @param int $label
481
     *
482
     * @return $this
483
     */
484
    public function setWidth($field = 8, $label = 2): self
485
    {
486
        $this->width = [
487
            'label' => $label,
488
            'field' => $field,
489
        ];
490
491
        return $this;
492
    }
493
494
    /**
495
     * Set the field options.
496
     *
497
     * @param array $options
498
     *
499
     * @return $this
500
     */
501 View Code Duplication
    public function options($options = [])
502
    {
503
        if ($options instanceof Arrayable) {
504
            $options = $options->toArray();
505
        }
506
507
        $this->options = array_merge($this->options, $options);
508
509
        return $this;
510
    }
511
512
    /**
513
     * Set the field option checked.
514
     *
515
     * @param array $checked
516
     *
517
     * @return $this
518
     */
519 View Code Duplication
    public function checked($checked = [])
520
    {
521
        if ($checked instanceof Arrayable) {
522
            $checked = $checked->toArray();
523
        }
524
525
        $this->checked = array_merge($this->checked, $checked);
526
527
        return $this;
528
    }
529
530
    /**
531
     * Add `required` attribute to current field if has required rule,
532
     * except file and image fields.
533
     *
534
     * @param array $rules
535
     */
536
    protected function addRequiredAttribute($rules)
537
    {
538
        if (!is_array($rules)) {
539
            return;
540
        }
541
542
        if (!in_array('required', $rules, true)) {
543
            return;
544
        }
545
546
        $this->setLabelClass(['asterisk']);
547
548
        // Only text field has `required` attribute.
549
        if (!$this instanceof Form\Field\Text) {
550
            return;
551
        }
552
553
        //do not use required attribute with tabs
554
        if ($this->form && $this->form->getTab()) {
555
            return;
556
        }
557
558
        $this->required();
559
    }
560
561
    /**
562
     * If has `required` rule, add required attribute to this field.
563
     */
564
    protected function addRequiredAttributeFromRules()
565
    {
566
        if ($this->data === null) {
567
            // Create page
568
            $rules = $this->creationRules ?: $this->rules;
569
        } else {
570
            // Update page
571
            $rules = $this->updateRules ?: $this->rules;
572
        }
573
574
        $this->addRequiredAttribute($rules);
575
    }
576
577
    /**
578
     * Format validation rules.
579
     *
580
     * @param array|string $rules
581
     *
582
     * @return array
583
     */
584
    protected function formatRules($rules): array
585
    {
586
        if (is_string($rules)) {
587
            $rules = array_filter(explode('|', $rules));
588
        }
589
590
        return array_filter((array) $rules);
591
    }
592
593
    /**
594
     * @param string|array|Closure $input
595
     * @param string|array         $original
596
     *
597
     * @return array|Closure
598
     */
599
    protected function mergeRules($input, $original)
600
    {
601
        if ($input instanceof Closure) {
602
            $rules = $input;
603
        } else {
604
            if (!empty($original)) {
605
                $original = $this->formatRules($original);
606
            }
607
608
            $rules = array_merge($original, $this->formatRules($input));
609
        }
610
611
        return $rules;
612
    }
613
614
    /**
615
     * Set the validation rules for the field.
616
     *
617
     * @param array|callable|string $rules
618
     * @param array                 $messages
619
     *
620
     * @return $this
621
     */
622 View Code Duplication
    public function rules($rules = null, $messages = []): self
623
    {
624
        $this->rules = $this->mergeRules($rules, $this->rules);
625
626
        $this->setValidationMessages('default', $messages);
627
628
        return $this;
629
    }
630
631
    /**
632
     * Set the update validation rules for the field.
633
     *
634
     * @param array|callable|string $rules
635
     * @param array                 $messages
636
     *
637
     * @return $this
638
     */
639 View Code Duplication
    public function updateRules($rules = null, $messages = []): self
640
    {
641
        $this->updateRules = $this->mergeRules($rules, $this->updateRules);
642
643
        $this->setValidationMessages('update', $messages);
644
645
        return $this;
646
    }
647
648
    /**
649
     * Set the creation validation rules for the field.
650
     *
651
     * @param array|callable|string $rules
652
     * @param array                 $messages
653
     *
654
     * @return $this
655
     */
656
    public function creationRules($rules = null, $messages = []): self
657
    {
658
        $this->creationRules = $this->mergeRules($rules, $this->creationRules);
659
660
        $this->setValidationMessages('creation', $messages);
661
662
        return $this;
663
    }
664
665
    /**
666
     * Set validation messages for column.
667
     *
668
     * @param string $key
669
     * @param array  $messages
670
     *
671
     * @return $this
672
     */
673
    public function setValidationMessages($key, array $messages): self
674
    {
675
        $this->validationMessages[$key] = $messages;
676
677
        return $this;
678
    }
679
680
    /**
681
     * Get validation messages for the field.
682
     *
683
     * @return array|mixed
684
     */
685
    public function getValidationMessages()
686
    {
687
        // Default validation message.
688
        $messages = $this->validationMessages['default'] ?? [];
689
690
        if (request()->isMethod('POST')) {
691
            $messages = $this->validationMessages['creation'] ?? $messages;
692
        } elseif (request()->isMethod('PUT')) {
693
            $messages = $this->validationMessages['update'] ?? $messages;
694
        }
695
696
        return $messages;
697
    }
698
699
    /**
700
     * Get field validation rules.
701
     *
702
     * @return string
703
     */
704
    protected function getRules()
705
    {
706
        if (request()->isMethod('POST')) {
707
            $rules = $this->creationRules ?: $this->rules;
708
        } elseif (request()->isMethod('PUT')) {
709
            $rules = $this->updateRules ?: $this->rules;
710
        } else {
711
            $rules = $this->rules;
712
        }
713
714
        if ($rules instanceof \Closure) {
715
            $rules = $rules->call($this, $this->form);
716
        }
717
718
        if (is_string($rules)) {
719
            $rules = array_filter(explode('|', $rules));
720
        }
721
722
        if (!$this->form || !$this->form instanceof Form) {
723
            return $rules;
724
        }
725
726
        if (!$id = $this->form->model()->getKey()) {
727
            return $rules;
728
        }
729
730
        if (is_array($rules)) {
731
            foreach ($rules as &$rule) {
732
                if (is_string($rule)) {
733
                    $rule = str_replace('{{id}}', $id, $rule);
734
                }
735
            }
736
        }
737
738
        return $rules;
739
    }
740
741
    /**
742
     * Remove a specific rule by keyword.
743
     *
744
     * @param string $rule
745
     *
746
     * @return void
747
     */
748
    protected function removeRule($rule)
749
    {
750
        if (is_array($this->rules)) {
751
            array_delete($this->rules, $rule);
752
753
            return;
754
        }
755
756
        if (!is_string($this->rules)) {
757
            return;
758
        }
759
760
        $pattern = "/{$rule}[^\|]?(\||$)/";
761
        $this->rules = preg_replace($pattern, '', $this->rules, -1);
762
    }
763
764
    /**
765
     * Set field validator.
766
     *
767
     * @param callable $validator
768
     *
769
     * @return $this
770
     */
771
    public function validator(callable $validator): self
772
    {
773
        $this->validator = $validator;
774
775
        return $this;
776
    }
777
778
    /**
779
     * Get key for error message.
780
     *
781
     * @return string|array
782
     */
783
    public function getErrorKey()
784
    {
785
        return $this->errorKey ?: $this->column;
786
    }
787
788
    /**
789
     * Set key for error message.
790
     *
791
     * @param string $key
792
     *
793
     * @return $this
794
     */
795
    public function setErrorKey($key): self
796
    {
797
        $this->errorKey = $key;
798
799
        return $this;
800
    }
801
802
    /**
803
     * Set or get value of the field.
804
     *
805
     * @param null $value
806
     *
807
     * @return mixed
808
     */
809
    public function value($value = null)
810
    {
811
        if ($value === null) {
812
            return $this->value ?? $this->getDefault();
813
        }
814
815
        $this->value = $value;
816
817
        return $this;
818
    }
819
820
    /**
821
     * Set or get data.
822
     *
823
     * @param array $data
824
     *
825
     * @return mixed
826
     */
827
    public function data(array $data = null)
828
    {
829
        if ($data === null) {
830
            return $this->data;
831
        }
832
833
        $this->data = $data;
834
835
        return $this;
836
    }
837
838
    /**
839
     * Set default value for field.
840
     *
841
     * @param $default
842
     *
843
     * @return $this
844
     */
845
    public function default($default): self
846
    {
847
        $this->default = $default;
848
849
        return $this;
850
    }
851
852
    /**
853
     * Get default value.
854
     *
855
     * @return mixed
856
     */
857
    public function getDefault()
858
    {
859
        if ($this->default instanceof \Closure) {
860
            return call_user_func($this->default, $this->form);
861
        }
862
863
        return $this->default;
864
    }
865
866
    /**
867
     * Set help block for current field.
868
     *
869
     * @param string $text
870
     * @param string $icon
871
     *
872
     * @return $this
873
     */
874
    public function help($text = '', $icon = 'fa-info-circle'): self
875
    {
876
        $this->help = compact('text', 'icon');
877
878
        return $this;
879
    }
880
881
    /**
882
     * Get column of the field.
883
     *
884
     * @return string|array
885
     */
886
    public function column()
887
    {
888
        return $this->column;
889
    }
890
891
    /**
892
     * Get label of the field.
893
     *
894
     * @return string
895
     */
896
    public function label(): string
897
    {
898
        return $this->label;
899
    }
900
901
    /**
902
     * Get original value of the field.
903
     *
904
     * @return mixed
905
     */
906
    public function original()
907
    {
908
        return $this->original;
909
    }
910
911
    /**
912
     * Get validator for this field.
913
     *
914
     * @param array $input
915
     *
916
     * @return bool|\Illuminate\Contracts\Validation\Validator|mixed
917
     */
918
    public function getValidator(array $input)
919
    {
920
        if ($this->validator) {
921
            return $this->validator->call($this, $input);
922
        }
923
924
        $rules = $attributes = [];
925
926
        if (!$fieldRules = $this->getRules()) {
927
            return false;
928
        }
929
930
        if (is_string($this->column)) {
931
            if (!Arr::has($input, $this->column)) {
932
                return false;
933
            }
934
935
            $input = $this->sanitizeInput($input, $this->column);
936
937
            $rules[$this->column] = $fieldRules;
938
            $attributes[$this->column] = $this->label;
939
        }
940
941
        if (is_array($this->column)) {
942
            foreach ($this->column as $key => $column) {
943
                if (!array_key_exists($column, $input)) {
944
                    continue;
945
                }
946
                $input[$column.$key] = Arr::get($input, $column);
947
                $rules[$column.$key] = $fieldRules;
948
                $attributes[$column.$key] = $this->label."[$column]";
949
            }
950
        }
951
952
        return \validator($input, $rules, $this->getValidationMessages(), $attributes);
953
    }
954
955
    /**
956
     * Sanitize input data.
957
     *
958
     * @param array  $input
959
     * @param string $column
960
     *
961
     * @return array
962
     */
963
    protected function sanitizeInput($input, $column)
964
    {
965
        if ($this instanceof Field\MultipleSelect) {
966
            $value = Arr::get($input, $column);
967
            Arr::set($input, $column, array_filter($value));
968
        }
969
970
        return $input;
971
    }
972
973
    /**
974
     * Add html attributes to elements.
975
     *
976
     * @param array|string $attribute
977
     * @param mixed        $value
978
     *
979
     * @return $this
980
     */
981
    public function attribute($attribute, $value = null): self
982
    {
983
        if (is_array($attribute)) {
984
            $this->attributes = array_merge($this->attributes, $attribute);
985
        } else {
986
            $this->attributes[$attribute] = (string) $value;
987
        }
988
989
        return $this;
990
    }
991
992
    /**
993
     * Remove html attributes from elements.
994
     *
995
     * @param array|string $attribute
996
     *
997
     * @return $this
998
     */
999
    public function removeAttribute($attribute): self
1000
    {
1001
        Arr::forget($this->attributes, $attribute);
1002
1003
        return $this;
1004
    }
1005
1006
    /**
1007
     * Set Field style.
1008
     *
1009
     * @param string $attr
1010
     * @param string $value
1011
     *
1012
     * @return $this
1013
     */
1014
    public function style($attr, $value): self
1015
    {
1016
        return $this->attribute('style', "{$attr}: {$value}");
1017
    }
1018
1019
    /**
1020
     * Set Field width.
1021
     *
1022
     * @param string $width
1023
     *
1024
     * @return $this
1025
     */
1026
    public function width($width): self
1027
    {
1028
        return $this->style('width', $width);
1029
    }
1030
1031
    /**
1032
     * Specifies a regular expression against which to validate the value of the input.
1033
     *
1034
     * @param string $regexp
1035
     *
1036
     * @return $this
1037
     */
1038
    public function pattern($regexp): self
1039
    {
1040
        return $this->attribute('pattern', $regexp);
1041
    }
1042
1043
    /**
1044
     * set the input filed required.
1045
     *
1046
     * @param bool $isLabelAsterisked
1047
     *
1048
     * @return $this
1049
     */
1050
    public function required($isLabelAsterisked = true): self
1051
    {
1052
        if ($isLabelAsterisked) {
1053
            $this->setLabelClass(['asterisk']);
1054
        }
1055
1056
        return $this->attribute('required', true);
1057
    }
1058
1059
    /**
1060
     * Set the field automatically get focus.
1061
     *
1062
     * @return $this
1063
     */
1064
    public function autofocus(): self
1065
    {
1066
        return $this->attribute('autofocus', true);
1067
    }
1068
1069
    /**
1070
     * Set the field as readonly mode.
1071
     *
1072
     * @return $this
1073
     */
1074
    public function readonly()
1075
    {
1076
        return $this->attribute('readonly', true);
1077
    }
1078
1079
    /**
1080
     * Set field as disabled.
1081
     *
1082
     * @return $this
1083
     */
1084
    public function disable(): self
1085
    {
1086
        return $this->attribute('disabled', true);
1087
    }
1088
1089
    /**
1090
     * Set field placeholder.
1091
     *
1092
     * @param string $placeholder
1093
     *
1094
     * @return $this
1095
     */
1096
    public function placeholder($placeholder = ''): self
1097
    {
1098
        $this->placeholder = $placeholder;
1099
1100
        return $this;
1101
    }
1102
1103
    /**
1104
     * Get placeholder.
1105
     *
1106
     * @return mixed
1107
     */
1108
    public function getPlaceholder()
1109
    {
1110
        return $this->placeholder ?: trans('admin.input').' '.$this->label;
1111
    }
1112
1113
    /**
1114
     * Add a divider after this field.
1115
     *
1116
     * @return $this
1117
     */
1118
    public function divider()
1119
    {
1120
        $this->form->divider();
1121
1122
        return $this;
1123
    }
1124
1125
    /**
1126
     * Prepare for a field value before update or insert.
1127
     *
1128
     * @param $value
1129
     *
1130
     * @return mixed
1131
     */
1132
    public function prepare($value)
1133
    {
1134
        return $value;
1135
    }
1136
1137
    /**
1138
     * Format the field attributes.
1139
     *
1140
     * @return string
1141
     */
1142 View Code Duplication
    protected function formatAttributes(): string
1143
    {
1144
        $html = [];
1145
1146
        foreach ($this->attributes as $name => $value) {
1147
            $html[] = $name.'="'.e($value).'"';
1148
        }
1149
1150
        return implode(' ', $html);
1151
    }
1152
1153
    /**
1154
     * @return $this
1155
     */
1156
    public function disableHorizontal(): self
1157
    {
1158
        $this->horizontal = false;
1159
1160
        return $this;
1161
    }
1162
1163
    /**
1164
     * @return array
1165
     */
1166
    public function getViewElementClasses(): array
1167
    {
1168
        if ($this->horizontal) {
1169
            return [
1170
                'label'      => "col-sm-{$this->width['label']} {$this->getLabelClass()}",
1171
                'field'      => "col-sm-{$this->width['field']}",
1172
                'form-group' => $this->getGroupClass(true),
1173
            ];
1174
        }
1175
1176
        return ['label' => $this->getLabelClass(), 'field' => '', 'form-group' => ''];
1177
    }
1178
1179
    /**
1180
     * Set form element class.
1181
     *
1182
     * @param string|array $class
1183
     *
1184
     * @return $this
1185
     */
1186
    public function setElementClass($class): self
1187
    {
1188
        $this->elementClass = array_merge($this->elementClass, (array) $class);
1189
1190
        return $this;
1191
    }
1192
1193
    /**
1194
     * Get element class.
1195
     *
1196
     * @return array
1197
     */
1198
    public function getElementClass(): array
1199
    {
1200
        if (!$this->elementClass) {
1201
            $name = $this->elementName ?: $this->formatName($this->column);
1202
1203
            $this->elementClass = (array) str_replace(['[', ']'], '_', $name);
1204
        }
1205
1206
        return $this->elementClass;
1207
    }
1208
1209
    /**
1210
     * Get element class string.
1211
     *
1212
     * @return mixed
1213
     */
1214 View Code Duplication
    public function getElementClassString()
1215
    {
1216
        $elementClass = $this->getElementClass();
1217
1218
        if (Arr::isAssoc($elementClass)) {
1219
            $classes = [];
1220
1221
            foreach ($elementClass as $index => $class) {
1222
                $classes[$index] = is_array($class) ? implode(' ', $class) : $class;
1223
            }
1224
1225
            return $classes;
1226
        }
1227
1228
        return implode(' ', $elementClass);
1229
    }
1230
1231
    /**
1232
     * Get element class selector.
1233
     *
1234
     * @return string|array
1235
     */
1236 View Code Duplication
    public function getElementClassSelector()
1237
    {
1238
        $elementClass = $this->getElementClass();
1239
1240
        if (Arr::isAssoc($elementClass)) {
1241
            $classes = [];
1242
1243
            foreach ($elementClass as $index => $class) {
1244
                $classes[$index] = '.'.(is_array($class) ? implode('.', $class) : $class);
1245
            }
1246
1247
            return $classes;
1248
        }
1249
1250
        return '.'.implode('.', $elementClass);
1251
    }
1252
1253
    /**
1254
     * Add the element class.
1255
     *
1256
     * @param $class
1257
     *
1258
     * @return $this
1259
     */
1260
    public function addElementClass($class): self
1261
    {
1262
        if (is_array($class) || is_string($class)) {
1263
            $this->elementClass = array_unique(array_merge($this->elementClass, (array) $class));
1264
        }
1265
1266
        return $this;
1267
    }
1268
1269
    /**
1270
     * Remove element class.
1271
     *
1272
     * @param $class
1273
     *
1274
     * @return $this
1275
     */
1276
    public function removeElementClass($class): self
1277
    {
1278
        $delClass = [];
1279
1280
        if (is_string($class) || is_array($class)) {
1281
            $delClass = (array) $class;
1282
        }
1283
1284
        foreach ($delClass as $del) {
1285
            if (($key = array_search($del, $this->elementClass, true)) !== false) {
1286
                unset($this->elementClass[$key]);
1287
            }
1288
        }
1289
1290
        return $this;
1291
    }
1292
1293
    /**
1294
     * Set form group class.
1295
     *
1296
     * @param string|array $class
1297
     *
1298
     * @return $this
1299
     */
1300
    public function setGroupClass($class): self
1301
    {
1302
        if (is_array($class)) {
1303
            $this->groupClass = array_merge($this->groupClass, $class);
1304
        } else {
1305
            $this->groupClass[] = $class;
1306
        }
1307
1308
        return $this;
1309
    }
1310
1311
    /**
1312
     * Get element class.
1313
     *
1314
     * @param bool $default
1315
     *
1316
     * @return string
1317
     */
1318
    protected function getGroupClass($default = false): string
1319
    {
1320
        return ($default ? 'form-group ' : '').implode(' ', array_filter($this->groupClass));
1321
    }
1322
1323
    /**
1324
     * reset field className.
1325
     *
1326
     * @param string $className
1327
     * @param string $resetClassName
1328
     *
1329
     * @return $this
1330
     */
1331
    public function resetElementClassName(string $className, string $resetClassName): self
1332
    {
1333
        if (($key = array_search($className, $this->getElementClass())) !== false) {
1334
            $this->elementClass[$key] = $resetClassName;
1335
        }
1336
1337
        return $this;
1338
    }
1339
1340
    /**
1341
     * Add variables to field view.
1342
     *
1343
     * @param array $variables
1344
     *
1345
     * @return $this
1346
     */
1347
    public function addVariables(array $variables = []): self
1348
    {
1349
        $this->variables = array_merge($this->variables, $variables);
1350
1351
        return $this;
1352
    }
1353
1354
    /**
1355
     * @return string
1356
     */
1357
    public function getLabelClass(): string
1358
    {
1359
        return implode(' ', $this->labelClass);
1360
    }
1361
1362
    /**
1363
     * @param array $labelClass
1364
     *
1365
     * @return self
1366
     */
1367
    public function setLabelClass(array $labelClass): self
1368
    {
1369
        $this->labelClass = $labelClass;
1370
1371
        return $this;
1372
    }
1373
1374
    /**
1375
     * Get the view variables of this field.
1376
     *
1377
     * @return array
1378
     */
1379
    public function variables(): array
1380
    {
1381
        return array_merge($this->variables, [
1382
            'id'          => $this->id,
1383
            'name'        => $this->elementName ?: $this->formatName($this->column),
1384
            'help'        => $this->help,
1385
            'class'       => $this->getElementClassString(),
1386
            'value'       => $this->value(),
1387
            'label'       => $this->label,
1388
            'viewClass'   => $this->getViewElementClasses(),
1389
            'column'      => $this->column,
1390
            'errorKey'    => $this->getErrorKey(),
1391
            'attributes'  => $this->formatAttributes(),
1392
            'placeholder' => $this->getPlaceholder(),
1393
        ]);
1394
    }
1395
1396
    /**
1397
     * Get view of this field.
1398
     *
1399
     * @return string
1400
     */
1401
    public function getView(): string
1402
    {
1403
        if (!empty($this->view)) {
1404
            return $this->view;
1405
        }
1406
1407
        $class = explode('\\', static::class);
1408
1409
        return 'admin::form.'.strtolower(end($class));
1410
    }
1411
1412
    /**
1413
     * Set view of current field.
1414
     *
1415
     * @param string $view
1416
     *
1417
     * @return string
1418
     */
1419
    public function setView($view): self
1420
    {
1421
        $this->view = $view;
1422
1423
        return $this;
1424
    }
1425
1426
    /**
1427
     * Get script of current field.
1428
     *
1429
     * @return string
1430
     */
1431
    public function getScript(): string
1432
    {
1433
        return $this->script;
1434
    }
1435
1436
    /**
1437
     * Set script of current field.
1438
     *
1439
     * @param string $script
1440
     *
1441
     * @return $this
1442
     */
1443
    public function setScript($script): self
1444
    {
1445
        $this->script = $script;
1446
1447
        return $this;
1448
    }
1449
1450
    /**
1451
     * To set this field should render or not.
1452
     *
1453
     * @param bool $display
1454
     *
1455
     * @return $this
1456
     */
1457
    public function setDisplay(bool $display): self
1458
    {
1459
        $this->display = $display;
1460
1461
        return $this;
1462
    }
1463
1464
    /**
1465
     * If this field should render.
1466
     *
1467
     * @return bool
1468
     */
1469
    protected function shouldRender(): bool
1470
    {
1471
        if (!$this->display) {
1472
            return false;
1473
        }
1474
1475
        return true;
1476
    }
1477
1478
    /**
1479
     * @param \Closure $callback
1480
     *
1481
     * @return \Encore\Admin\Form\Field
1482
     */
1483
    public function with(Closure $callback): self
1484
    {
1485
        $this->callback = $callback;
1486
1487
        return $this;
1488
    }
1489
1490
    /**
1491
     * Render this filed.
1492
     *
1493
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1494
     */
1495
    public function render()
1496
    {
1497
        if (!$this->shouldRender()) {
1498
            return '';
1499
        }
1500
1501
        $this->addRequiredAttributeFromRules();
1502
1503
        if ($this->callback instanceof Closure) {
1504
            $this->value = $this->callback->call($this->form->model(), $this->value, $this);
1505
        }
1506
1507
        Admin::script($this->script);
1508
1509
        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 1509 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
1510
    }
1511
1512
    /**
1513
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1514
     */
1515
    protected function fieldRender()
1516
    {
1517
        return self::render();
1518
    }
1519
1520
    /**
1521
     * @return string
1522
     */
1523
    public function __toString()
1524
    {
1525
        return $this->render()->render();
0 ignored issues
show
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...
1526
    }
1527
}
1528