Completed
Pull Request — master (#2254)
by
unknown
11:30
created

Select::options()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 5
nop 1
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Form\Field;
4
5
use Encore\Admin\Facades\Admin;
6
use Encore\Admin\Form\Field;
7
use Illuminate\Contracts\Support\Arrayable;
8
use Illuminate\Support\Str;
9
10
class Select extends Field
11
{
12
    /**
13
     * @var array
14
     */
15
    protected static $css = [
16
        '/vendor/laravel-admin/AdminLTE/plugins/select2/select2.min.css',
17
    ];
18
19
    /**
20
     * @var array
21
     */
22
    protected static $js = [
23
        '/vendor/laravel-admin/AdminLTE/plugins/select2/select2.full.min.js',
24
    ];
25
26
    /**
27
     * @var array
28
     */
29
    protected $groups = [];
30
31
    /**
32
     * @var array
33
     */
34
    protected $config = [];
35
36
    /**
37
     * Set options.
38
     *
39
     * @param array|callable|string $options
40
     *
41
     * @return $this|mixed
42
     */
43
    public function options($options = [])
44
    {
45
        // remote options
46
        if (is_string($options)) {
47
            return $this->loadRemoteOptions(...func_get_args());
0 ignored issues
show
Documentation introduced by
func_get_args() is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
48
        }
49
50
        if ($options instanceof Arrayable) {
51
            $options = $options->toArray();
52
        }
53
54
        if (is_callable($options)) {
55
            $this->options = $options;
0 ignored issues
show
Documentation Bug introduced by
It seems like $options of type callable is incompatible with the declared type array of property $options.

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...
56
        } else {
57
            $this->options = (array) $options;
58
        }
59
60
        return $this;
61
    }
62
63
    /**
64
     * @param array $groups
65
     */
66
67
    /**
68
     * Set option groups.
69
     *
70
     * eg: $group = [
71
     *        [
72
     *        'label' => 'xxxx',
73
     *        'options' => [
74
     *            1 => 'foo',
75
     *            2 => 'bar',
76
     *            ...
77
     *        ],
78
     *        ...
79
     *     ]
80
     *
81
     * @param array $groups
82
     *
83
     * @return $this
84
     */
85
    public function groups(array $groups)
86
    {
87
        $this->groups = $groups;
88
89
        return $this;
90
    }
91
92
    /**
93
     * Load options for other select on change.
94
     *
95
     * @param string $field
96
     * @param string $sourceUrl
97
     * @param string $idField
98
     * @param string $textField
99
     *
100
     * @return $this
101
     */
102
    public function load($field, $sourceUrl, $idField = 'id', $textField = 'text')
103
    {
104
        if (Str::contains($field, '.')) {
105
            $field = $this->formatName($field);
106
            $class = str_replace(['[', ']'], '_', $field);
107
        } else {
108
            $class = $field;
109
        }
110
111
        $script = <<<EOT
112
$(document).off('change', "{$this->getElementClassSelector()}");
113
$(document).on('change', "{$this->getElementClassSelector()}", function () {
114
    var target = $(this).closest('.fields-group').find(".$class");
115
    $.get("$sourceUrl?q="+this.value, function (data) {
116
        target.find("option").remove();
117
        $(target).select2({
118
            data: $.map(data, function (d) {
119
                d.id = d.$idField;
120
                d.text = d.$textField;
121
                return d;
122
            })
123
        }).trigger('change');
124
    });
125
});
126
EOT;
127
128
        Admin::script($script);
129
130
        return $this;
131
    }
132
133
    /**
134
     * Load options for other selects on change.
135
     *
136
     * @param string $fields
137
     * @param string $sourceUrls
138
     * @param string $idField
139
     * @param string $textField
140
     *
141
     * @return $this
142
     */
143
    public function loads($fields = [], $sourceUrls = [], $idField = 'id', $textField = 'text')
144
    {
145
        $fieldsStr = implode('.', $fields);
146
        $urlsStr = implode('^', $sourceUrls);
147
        $script = <<<EOT
148
var fields = '$fieldsStr'.split('.');
149
var urls = '$urlsStr'.split('^');
150
151
var refreshOptions = function(url, target) {
152
    $.get(url).then(function(data) {
153
        target.find("option").remove();
154
        $(target).select2({
155
            data: $.map(data, function (d) {
156
                d.id = d.$idField;
157
                d.text = d.$textField;
158
                return d;
159
            })
160
        }).trigger('change');
161
    });
162
};
163
164
$(document).off('change', "{$this->getElementClassSelector()}");
165
$(document).on('change', "{$this->getElementClassSelector()}", function () {
166
    var _this = this;
167
    var promises = [];
168
169
    fields.forEach(function(field, index){
170
        var target = $(_this).closest('.fields-group').find('.' + fields[index]);
171
        promises.push(refreshOptions(urls[index] + "?q="+ _this.value, target));
172
    });
173
174
    $.when(promises).then(function() {
175
        console.log('开始更新其它select的选择options');
176
    });
177
});
178
EOT;
179
180
        Admin::script($script);
181
182
        return $this;
183
    }
184
185
    /**
186
     * Load options from remote.
187
     *
188
     * @param string $url
189
     * @param array  $parameters
190
     * @param array  $options
191
     *
192
     * @return $this
193
     */
194 View Code Duplication
    protected function loadRemoteOptions($url, $parameters = [], $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...
195
    {
196
        $ajaxOptions = [
197
            'url' => $url.'?'.http_build_query($parameters),
198
        ];
199
200
        $ajaxOptions = json_encode(array_merge($ajaxOptions, $options));
201
202
        $this->script = <<<EOT
203
204
$.ajax($ajaxOptions).done(function(data) {
205
  $("{$this->getElementClassSelector()}").select2({data: data});
206
});
207
208
EOT;
209
210
        return $this;
211
    }
212
213
    /**
214
     * Load options from ajax results.
215
     *
216
     * @param string $url
217
     * @param $idField
218
     * @param $textField
219
     *
220
     * @return $this
221
     */
222
    public function ajax($url, $idField = 'id', $textField = 'text')
223
    {
224
        $this->script = <<<EOT
225
226
$("{$this->getElementClassSelector()}").select2({
227
  ajax: {
228
    url: "$url",
229
    dataType: 'json',
230
    delay: 250,
231
    data: function (params) {
232
      return {
233
        q: params.term,
234
        page: params.page
235
      };
236
    },
237
    processResults: function (data, params) {
238
      params.page = params.page || 1;
239
240
      return {
241
        results: $.map(data.data, function (d) {
242
                   d.id = d.$idField;
243
                   d.text = d.$textField;
244
                   return d;
245
                }),
246
        pagination: {
247
          more: data.next_page_url
248
        }
249
      };
250
    },
251
    cache: true
252
  },
253
  minimumInputLength: 1,
254
  escapeMarkup: function (markup) {
255
      return markup;
256
  }
257
});
258
259
EOT;
260
261
        return $this;
262
    }
263
264
    /**
265
     * Set config for select2.
266
     *
267
     * all configurations see https://select2.org/configuration/options-api
268
     *
269
     * @param string $key
270
     * @param mixed  $val
271
     *
272
     * @return $this
273
     */
274
    public function config($key, $val)
275
    {
276
        $this->config[$key] = $val;
277
278
        return $this;
279
    }
280
281
    /**
282
     * {@inheritdoc}
283
     */
284
    public function render()
285
    {
286
        $configs = array_merge([
287
            'allowClear'  => true,
288
            'placeholder' => $this->label,
289
        ], $this->config);
290
291
        $configs = json_encode($configs);
292
293
        if (empty($this->script)) {
294
            $this->script = "$(\"{$this->getElementClassSelector()}\").select2($configs);";
295
        }
296
297
        if ($this->options instanceof \Closure) {
298
            if ($this->form) {
299
                $this->options = $this->options->bindTo($this->form->model());
300
            }
301
302
            $this->options(call_user_func($this->options, $this->value));
303
        }
304
305
        $this->options = array_filter($this->options);
306
307
        $this->addVariables([
308
            'options' => $this->options,
309
            'groups'  => $this->groups,
310
        ]);
311
312
        return parent::render();
0 ignored issues
show
Bug Compatibility introduced by
The expression parent::render(); of type string|Illuminate\View\V...\Contracts\View\Factory adds the type Illuminate\Contracts\View\Factory to the return on line 312 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
313
    }
314
}
315