Completed
Pull Request — master (#2396)
by
unknown
03:11
created

Select::model()   B

Complexity

Conditions 11
Paths 1

Size

Total Lines 36

Duplication

Lines 36
Ratio 100 %

Importance

Changes 0
Metric Value
cc 11
nc 1
nop 3
dl 36
loc 36
rs 7.3166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
            // reload selected
48
            if (class_exists($options) && in_array('Illuminate\Database\Eloquent\Model', class_parents($options))) {
49
                return $this->model(...func_get_args());
0 ignored issues
show
Documentation introduced by
func_get_args() is of type array, but the function expects a object<Encore\Admin\Form...atabase\Eloquent\Model>.

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...
50
            }
51
52
            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...
53
        }
54
55
        if ($options instanceof Arrayable) {
56
            $options = $options->toArray();
57
        }
58
59
        if (is_callable($options)) {
60
            $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...
61
        } else {
62
            $this->options = (array) $options;
63
        }
64
65
        return $this;
66
    }
67
68
    /**
69
     * @param array $groups
70
     */
71
72
    /**
73
     * Set option groups.
74
     *
75
     * eg: $group = [
76
     *        [
77
     *        'label' => 'xxxx',
78
     *        'options' => [
79
     *            1 => 'foo',
80
     *            2 => 'bar',
81
     *            ...
82
     *        ],
83
     *        ...
84
     *     ]
85
     *
86
     * @param array $groups
87
     *
88
     * @return $this
89
     */
90
    public function groups(array $groups)
91
    {
92
        $this->groups = $groups;
93
94
        return $this;
95
    }
96
97
    /**
98
     * Load options for other select on change.
99
     *
100
     * @param string $field
101
     * @param string $sourceUrl
102
     * @param string $idField
103
     * @param string $textField
104
     *
105
     * @return $this
106
     */
107
    public function load($field, $sourceUrl, $idField = 'id', $textField = 'text')
108
    {
109
        if (Str::contains($field, '.')) {
110
            $field = $this->formatName($field);
111
            $class = str_replace(['[', ']'], '_', $field);
112
        } else {
113
            $class = $field;
114
        }
115
116
        $script = <<<EOT
117
$(document).off('change', "{$this->getElementClassSelector()}");
118
$(document).on('change', "{$this->getElementClassSelector()}", function () {
119
    var target = $(this).closest('.fields-group').find(".$class");
120
    $.get("$sourceUrl?q="+this.value, function (data) {
121
        target.find("option").remove();
122
        $(target).select2({
123
            data: $.map(data, function (d) {
124
                d.id = d.$idField;
125
                d.text = d.$textField;
126
                return d;
127
            })
128
        }).trigger('change');
129
    });
130
});
131
EOT;
132
133
        Admin::script($script);
134
135
        return $this;
136
    }
137
138
    /**
139
     * Load options for other selects on change.
140
     *
141
     * @param string $fields
142
     * @param string $sourceUrls
143
     * @param string $idField
144
     * @param string $textField
145
     *
146
     * @return $this
147
     */
148
    public function loads($fields = [], $sourceUrls = [], $idField = 'id', $textField = 'text')
149
    {
150
        $fieldsStr = implode('.', $fields);
151
        $urlsStr = implode('^', $sourceUrls);
152
        $script = <<<EOT
153
var fields = '$fieldsStr'.split('.');
154
var urls = '$urlsStr'.split('^');
155
156
var refreshOptions = function(url, target) {
157
    $.get(url).then(function(data) {
158
        target.find("option").remove();
159
        $(target).select2({
160
            data: $.map(data, function (d) {
161
                d.id = d.$idField;
162
                d.text = d.$textField;
163
                return d;
164
            })
165
        }).trigger('change');
166
    });
167
};
168
169
$(document).off('change', "{$this->getElementClassSelector()}");
170
$(document).on('change', "{$this->getElementClassSelector()}", function () {
171
    var _this = this;
172
    var promises = [];
173
174
    fields.forEach(function(field, index){
175
        var target = $(_this).closest('.fields-group').find('.' + fields[index]);
176
        promises.push(refreshOptions(urls[index] + "?q="+ _this.value, target));
177
    });
178
179
    $.when(promises).then(function() {
180
        console.log('开始更新其它select的选择options');
181
    });
182
});
183
EOT;
184
185
        Admin::script($script);
186
187
        return $this;
188
    }
189
190
    /**
191
     * Load options from current selected resource(s).
192
     *
193
     * @param Illuminate\Database\Eloquent\Model $model
194
     * @param string                             $idField
195
     * @param string                             $textField
196
     *
197
     * @return $this
198
     */
199 View Code Duplication
    public function model($model, $idField = 'id', $textField = 'name')
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...
200
    {
201
        $this->options = function ($resource) use ($model, $idField, $textField) {
0 ignored issues
show
Documentation Bug introduced by
It seems like function ($resource) use...} return array(); } of type object<Closure> 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...
202
            if (null == $resource) {
203
                return [];
204
            }
205
206
            if (is_array($resource) && !empty($resource) && isset($resource[0]['id'])) {
207
                $resource = array_map(function ($res) {
208
                    return $res['id'];
209
                }, $resource);
210
            } elseif (is_array($resource) && !empty($resource) && isset($resource['id'])) {
211
                $resource = $resource['id'];
212
            }
213
214
            $model = $model::find($resource);
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $model, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
215
216
            if ($model) {
217
                if ($model instanceof \Illuminate\Support\Collection) {
218
                    $results = [];
219
220
                    foreach ($model as $result) {
221
                        $results[$result->{$idField}] = $result->{$textField};
222
                    }
223
224
                    return $results;
225
                }
226
227
                return [$model->{$idField} => $model->{$textField}];
228
            }
229
230
            return [];
231
        };
232
233
        return $this;
234
    }
235
236
    /**
237
     * Load options from remote.
238
     *
239
     * @param string $url
240
     * @param array  $parameters
241
     * @param array  $options
242
     *
243
     * @return $this
244
     */
245 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...
246
    {
247
        $ajaxOptions = [
248
            'url' => $url.'?'.http_build_query($parameters),
249
        ];
250
251
        $ajaxOptions = json_encode(array_merge($ajaxOptions, $options));
252
253
        $this->script = <<<EOT
254
255
$.ajax($ajaxOptions).done(function(data) {
256
257
  var select = $("{$this->getElementClassSelector()}");
258
259
  select.select2({data: data});
260
  
261
  var value = select.data('value') + '';
262
  
263
  if (value) {
264
    value = value.split(',');
265
    select.select2('val', value);
266
  }
267
});
268
269
EOT;
270
271
        return $this;
272
    }
273
274
    /**
275
     * Load options from ajax results.
276
     *
277
     * @param string $url
278
     * @param $idField
279
     * @param $textField
280
     *
281
     * @return $this
282
     */
283 View Code Duplication
    public function ajax($url, $idField = 'id', $textField = 'text')
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...
284
    {
285
        $configs = array_merge([
286
            'allowClear'         => true,
287
            'placeholder'        => $this->label,
288
            'minimumInputLength' => 1,
289
        ], $this->config);
290
291
        $configs = json_encode($configs);
292
        $configs = substr($configs, 1, strlen($configs) - 2);
293
294
        $this->script = <<<EOT
295
296
$("{$this->getElementClassSelector()}").select2({
297
  ajax: {
298
    url: "$url",
299
    dataType: 'json',
300
    delay: 250,
301
    data: function (params) {
302
      return {
303
        q: params.term,
304
        page: params.page
305
      };
306
    },
307
    processResults: function (data, params) {
308
      params.page = params.page || 1;
309
310
      return {
311
        results: $.map(data.data, function (d) {
312
                   d.id = d.$idField;
313
                   d.text = d.$textField;
314
                   return d;
315
                }),
316
        pagination: {
317
          more: data.next_page_url
318
        }
319
      };
320
    },
321
    cache: true
322
  },
323
  $configs,
324
  escapeMarkup: function (markup) {
325
      return markup;
326
  }
327
});
328
329
EOT;
330
331
        return $this;
332
    }
333
334
    /**
335
     * Set config for select2.
336
     *
337
     * all configurations see https://select2.org/configuration/options-api
338
     *
339
     * @param string $key
340
     * @param mixed  $val
341
     *
342
     * @return $this
343
     */
344
    public function config($key, $val)
345
    {
346
        $this->config[$key] = $val;
347
348
        return $this;
349
    }
350
351
    /**
352
     * {@inheritdoc}
353
     */
354
    public function render()
355
    {
356
        $configs = array_merge([
357
            'allowClear'  => true,
358
            'placeholder' => $this->label,
359
        ], $this->config);
360
361
        $configs = json_encode($configs);
362
363
        if (empty($this->script)) {
364
            $this->script = "$(\"{$this->getElementClassSelector()}\").select2($configs);";
365
        }
366
367
        if ($this->options instanceof \Closure) {
368
            if ($this->form) {
369
                $this->options = $this->options->bindTo($this->form->model());
370
            }
371
372
            $this->options(call_user_func($this->options, $this->value));
373
        }
374
375
        $this->options = array_filter($this->options, 'strlen');
376
377
        $this->addVariables([
378
            'options' => $this->options,
379
            'groups'  => $this->groups,
380
        ]);
381
382
        $this->attribute('data-value', implode(',', (array) $this->value()));
383
384
        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 384 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
385
    }
386
}
387