Completed
Pull Request — master (#2113)
by
unknown
03:06
created

Select::ajaxLoad()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 56
Code Lines 16

Duplication

Lines 6
Ratio 10.71 %

Importance

Changes 0
Metric Value
cc 2
eloc 16
nc 2
nop 4
dl 6
loc 56
rs 9.7251
c 0
b 0
f 0

How to fix   Long Method   

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
	 * @var array
13
	 */
14
	protected static $css = [
15
		'/vendor/laravel-admin/AdminLTE/plugins/select2/select2.min.css',
16
	];
17
18
	/**
19
	 * @var array
20
	 */
21
	protected static $js = [
22
		'/vendor/laravel-admin/AdminLTE/plugins/select2/select2.full.min.js',
23
	];
24
25
	/**
26
	 * @var array
27
	 */
28
	protected $groups = [];
29
30
	/**
31
	 * @var array
32
	 */
33
	protected $config = [];
34
35
	/**
36
	 * Set options.
37
	 *
38
	 * @param array|callable|string $options
39
	 *
40
	 * @return $this|mixed
41
	 */
42
	public function options($options = []) {
43
		// remote options
44
		if (is_string($options)) {
45
			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...
46
		}
47
48
		if ($options instanceof Arrayable) {
49
			$options = $options->toArray();
50
		}
51
52
		if (is_callable($options)) {
53
			$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...
54
		} else {
55
			$this->options = (array) $options;
56
		}
57
58
		return $this;
59
	}
60
61
	/**
62
	 * @param array $groups
63
	 */
64
65
	/**
66
	 * Set option groups.
67
	 *
68
	 * eg: $group = [
69
	 *        [
70
	 *        'label' => 'xxxx',
71
	 *        'options' => [
72
	 *            1 => 'foo',
73
	 *            2 => 'bar',
74
	 *            ...
75
	 *        ],
76
	 *        ...
77
	 *     ]
78
	 *
79
	 * @param array $groups
80
	 *
81
	 * @return $this
82
	 */
83
	public function groups(array $groups) {
84
		$this->groups = $groups;
85
86
		return $this;
87
	}
88
89
	/**
90
	 * Load options for other select on change.
91
	 *
92
	 * @param string $field
93
	 * @param string $sourceUrl
94
	 * @param string $idField
95
	 * @param string $textField
96
	 *
97
	 * @return $this
98
	 */
99
	public function load($field, $sourceUrl, $idField = 'id', $textField = 'text') {
100 View Code Duplication
		if (Str::contains($field, '.')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
101
			$field = $this->formatName($field);
102
			$class = str_replace(['[', ']'], '_', $field);
103
		} else {
104
			$class = $field;
105
		}
106
107
		$script = <<<EOT
108
$(document).off('change', "{$this->getElementClassSelector()}");
109
$(document).on('change', "{$this->getElementClassSelector()}", function () {
110
    var target = $(this).closest('.fields-group').find(".$class");
111
    $.get("$sourceUrl?q="+this.value, function (data) {
112
        target.find("option").remove();
113
        $(target).select2({
114
            data: $.map(data, function (d) {
115
                d.id = d.$idField;
116
                d.text = d.$textField;
117
                return d;
118
            })
119
        }).trigger('change');
120
    });
121
});
122
EOT;
123
124
		Admin::script($script);
125
126
		return $this;
127
	}
128
129
	/**
130
	 * Load options for other selects on change.
131
	 *
132
	 * @param string $fields
133
	 * @param string $sourceUrls
134
	 * @param string $idField
135
	 * @param string $textField
136
	 *
137
	 * @return $this
138
	 */
139
	public function loads($fields = [], $sourceUrls = [], $idField = 'id', $textField = 'text') {
140
		$fieldsStr = implode('.', $fields);
141
		$urlsStr = implode('^', $sourceUrls);
142
		$script = <<<EOT
143
var fields = '$fieldsStr'.split('.');
144
var urls = '$urlsStr'.split('^');
145
146
var refreshOptions = function(url, target) {
147
    $.get(url).then(function(data) {
148
        target.find("option").remove();
149
        $(target).select2({
150
            data: $.map(data, function (d) {
151
                d.id = d.$idField;
152
                d.text = d.$textField;
153
                return d;
154
            })
155
        }).trigger('change');
156
    });
157
};
158
159
$(document).off('change', "{$this->getElementClassSelector()}");
160
$(document).on('change', "{$this->getElementClassSelector()}", function () {
161
    var _this = this;
162
    var promises = [];
163
164
    fields.forEach(function(field, index){
165
        var target = $(_this).closest('.fields-group').find('.' + fields[index]);
166
        promises.push(refreshOptions(urls[index] + "?q="+ _this.value, target));
167
    });
168
169
    $.when(promises).then(function() {
170
        console.log('开始更新其它select的选择options');
171
    });
172
});
173
EOT;
174
175
		Admin::script($script);
176
177
		return $this;
178
	}
179
180
	/**
181
	 * Load options from remote.
182
	 *
183
	 * @param string $url
184
	 * @param array  $parameters
185
	 * @param array  $options
186
	 *
187
	 * @return $this
188
	 */
189 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...
190
		$ajaxOptions = [
191
			'url' => $url . '?' . http_build_query($parameters),
192
		];
193
194
		$ajaxOptions = json_encode(array_merge($ajaxOptions, $options));
195
196
		$this->script = <<<EOT
197
198
$.ajax($ajaxOptions).done(function(data) {
199
  $("{$this->getElementClassSelector()}").select2({data: data});
200
});
201
202
EOT;
203
204
		return $this;
205
	}
206
207
	/**
208
	 * Load options from ajax results.
209
	 *
210
	 * @param string $url
211
	 * @param $idField
212
	 * @param $textField
213
	 *
214
	 * @return $this
215
	 */
216
	public function ajax($url, $idField = 'id', $textField = 'text') {
217
		$this->script = <<<EOT
218
219
$("{$this->getElementClassSelector()}").select2({
220
  ajax: {
221
    url: "$url",
222
    dataType: 'json',
223
    delay: 250,
224
    data: function (params) {
225
      return {
226
        q: params.term,
227
        page: params.page
228
      };
229
    },
230
    processResults: function (data, params) {
231
      params.page = params.page || 1;
232
233
      return {
234
        results: $.map(data.data, function (d) {
235
                   d.id = d.$idField;
236
                   d.text = d.$textField;
237
                   return d;
238
                }),
239
        pagination: {
240
          more: data.next_page_url
241
        }
242
      };
243
    },
244
    cache: true
245
  },
246
  minimumInputLength: 1,
247
  escapeMarkup: function (markup) {
248
      return markup;
249
  }
250
});
251
252
EOT;
253
254
		return $this;
255
	}
256
257
	/**
258
	 * Set config for select2.
259
	 *
260
	 * all configurations see https://select2.org/configuration/options-api
261
	 *
262
	 * @param string $key
263
	 * @param mixed  $val
264
	 *
265
	 * @return $this
266
	 */
267
	public function config($key, $val) {
268
		$this->config[$key] = $val;
269
270
		return $this;
271
	}
272
273
	/**
274
	 * {@inheritdoc}
275
	 */
276
	public function render() {
277
		$configs = array_merge([
278
			'allowClear' => true,
279
			'placeholder' => $this->label,
280
		], $this->config);
281
282
		$configs = json_encode($configs);
283
284
		if (empty($this->script)) {
285
			$this->script = "$(\"{$this->getElementClassSelector()}\").select2($configs);";
286
		}
287
288
		if ($this->options instanceof \Closure) {
289
			if ($this->form) {
290
				$this->options = $this->options->bindTo($this->form->model());
291
			}
292
293
			$this->options(call_user_func($this->options, $this->value));
294
		}
295
296
		$this->options = array_filter($this->options);
297
298
		return parent::render()->with([
0 ignored issues
show
Bug introduced by
The method with 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...
299
			'options' => $this->options,
300
			'groups' => $this->groups,
301
		]);
302
	}
303
304
	/**
305
	 * Select Level 2 linkage, support ajax paging.
306
	 *
307
	 * @param string $field    parent field name
308
	 * @param string $sourceUrl resource route
309
	 * @param string $idField
310
	 * @param string $textField
311
	 *
312
	 * @return $this
313
	 */
314
	public function ajaxLoad($field, $sourceUrl, $idField = 'id', $textField = 'text') {
315 View Code Duplication
		if (Str::contains($field, '.')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
316
			$field = $this->formatName($field);
317
			$class = str_replace(['[', ']'], '_', $field);
318
		} else {
319
			$class = $field;
320
		}
321
322
		$this->script = <<<EOT
323
        var current=$("{$this->getElementClassSelector()}");
324
        var target = current.closest('.fields-group').find(".$class");
325
326
        var init=function (){
327
            current.select2({
328
                ajax: {
329
                  url: "$sourceUrl",
330
                  dataType: 'json',
331
                  delay: 250,
332
                  data: function (params) {
333
                    return {
334
                      father_value:target.val(),
335
                      q: params.term,
336
                      page: params.page
337
                    };
338
                  },
339
                  processResults: function (data, params) {
340
                    params.page = params.page || 1;
341
                    return {
342
                      results: $.map(data.data, function (d) {
343
                                 d.id = d.$idField;
344
                                 d.text = d.$textField;
345
                                 return d;
346
                              }),
347
                      pagination: {
348
                        more: data.next_page_url
349
                      }
350
                    };
351
                  },
352
                  cache: true
353
                },
354
                allowClear: true,
355
                placeholder: "{$this->label}",
356
                minimumInputLength: 1,
357
                escapeMarkup: function (markup) {
358
                    return markup;
359
                }
360
            });
361
        }
362
        init();
363
        $(document).on('change', "{$this->getElementClassSelector()}", function () {
364
           init();
365
        });
366
EOT;
367
368
		return $this;
369
	}
370
371
}
372