Completed
Push — master ( af68b1...5ddddb )
by Andrii
02:19
created

Combo::init()   F

Complexity

Conditions 10
Paths 256

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 10.3936

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 16
cts 19
cp 0.8421
rs 3.1304
c 0
b 0
f 0
cc 10
eloc 18
nc 256
nop 0
crap 10.3936

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
 * Combo widget for Yii2
4
 *
5
 * @link      https://github.com/hiqdev/yii2-combo
6
 * @package   yii2-combo
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\combo;
12
13
use Yii;
14
use yii\helpers\ArrayHelper;
15
use yii\helpers\Html;
16
use yii\helpers\Json;
17
use yii\helpers\Url;
18
use yii\web\JsExpression;
19
use yii\web\View;
20
use yii\widgets\InputWidget;
21
22
/**
23
 * Widget Combo.
24
 *
25
 * @property mixed $return see [[_return]]
26
 * @property mixed $rename see [[_rename]]
27
 * @property mixed $filter see [[_filter]]
28
 * @property mixed $pluginOptions see [[_pluginOptions]]
29
 * @property mixed $primaryFilter see [[_primaryFilter]]
30
 * @property mixed hasId
31
 */
32
class Combo extends InputWidget
33
{
34
    /**
35
     * @var array the url that will be passed to [[Url::to()]] method to create the request URL
36
     */
37
    public $url;
38
39
    /**
40
     * @var string the type of the field.
41
     * Usual should be module/comboName.
42
     * For example: client/client, hosting/account, domain/domain.
43
     * In case of the combo overriding with some specific filters,
44
     * the type should represent the filter.
45
     * For example: if the hosting/service combo is extended with filter
46
     * to show only DB services, the type should be hosting/service/db or hosting/dbService.
47
     * The decision of the style depends on overall code style and readability
48
     */
49
    public $type;
50
51
    /**
52
     * @var string the name of the representative field in the model.
53
     * Used by [[getPrimaryFilter]] to create the name of the filtering field
54
     * @see getPrimaryFilter()
55
     */
56
    public $name;
57
58
    /**
59
     * @var string md5 of the configuration.
60
     * Appears only after the combo registration in [[register]]
61
     * @see register()
62
     */
63
    public $configId;
64
65
    /**
66
     * @var array the HTML options for the input element
67
     */
68
    public $inputOptions = [];
69
70
    /**
71
     * @var string the outer element selector, that holds all of related Combos
72
     */
73
    public $formElementSelector = 'form';
74
75
    /**
76
     * @var string the language. Default is application language
77
     */
78
    public $language;
79
80
    /**
81
     * @var bool allow multiple selection
82
     */
83
    public $multiple;
84
85
    /**
86
     * @var array
87
     */
88
    public $current;
89
90
    /**
91
     * @var mixed returning arguments
92
     * Example:
93
     *
94
     * ```
95
     *  ['id', 'password', 'another_column']
96
     * ```
97
     *
98
     * @see getReturn()
99
     * @see setReturn()
100
     */
101
    protected $_return;
102
103
    /**
104
     * @var array renamed arguments
105
     * Example:
106
     *
107
     * ```
108
     *  [
109
     *      'new_col_name' => 'old_col_name',
110
     *      'text' => 'login',
111
     *      'deep' => 'array.subarray.value' // can extract some value from an array
112
     *  ]
113
     * ```
114
     *
115
     * @see getName()
116
     * @see setName()
117
     */
118
    protected $_rename;
119
120
    /**
121
     * @var array the static filters
122
     * Example:
123
     *
124
     * ```
125
     * [
126
     *      'someStaticValue' => ['format' => 'the_value'],
127
     *      'type'            => ['format' => 'seller'],
128
     *      'is_active'       => [
129
     *          'field'  => 'server',
130
     *          'format' => new JsExpression('function (id, text, field) {
131
     *              if (field.isSet()) {
132
     *                  return 1;
133
     *              }
134
     *          }'),
135
     *      ]
136
     * ]
137
     * @see setFilter()
138
     * @see getFilter()
139
     */
140
    protected $_filter = [];
141
142
    /**
143
     * @var string the name of the primary filter. Default: [[name]]_like
144
     * @see getPrimaryFilter
145
     * @see setPrimaryFilter
146
     */
147
    protected $_primaryFilter;
148
149
    /**
150
     * @var boolean|string whether the combo has a primary key
151
     *   null (default) - decision will be taken automatically.
152
     *                    In case when [[attribute]] has the `_id` postfix, this property will be treated as `true`
153
     *            false - the combo does not have an id. Meaning the value of the attribute will be used as the ID
154
     *           string - the explicit name of the ID attribute
155
     */
156
    public $_hasId;
157
158
    /**
159
     * Options that will be passed to the plugin.
160
     *
161
     * @var array
162
     * @see getPluginOptions()
163
     */
164
    public $_pluginOptions = [];
165
166
    /** {@inheritdoc} */
167 2
    public function init()
168
    {
169 2
        parent::init();
170
171
        // Set language
172 2
        if ($this->language === null && ($language = Yii::$app->language) !== 'en-US') {
173 2
            $this->language = substr($language, 0, 2);
174
        }
175 2
        if (!$this->_return) {
176 2
            $this->return = ['id'];
177
        }
178 2
        if (!$this->rename) {
179 2
            $this->rename = ['text' => $this->name];
180
        }
181 2
        if (!$this->inputOptions['id']) {
182
            $this->inputOptions['id'] = Html::getInputId($this->model, $this->attribute);
183
        }
184 2
        if ($this->multiple) {
185
            $this->inputOptions['multiple'] = true;
186
        }
187 2
        if ($this->inputOptions['readonly']) {
188
            // According to the HTML specification, the `select` element does not support
189
            // property `readonly`. Solution: render `readonly` field as disabled and prepend hidden
190
            // input to submit the attribute value.
191
            $this->inputOptions['disabled'] = true;
192
        }
193 2
        if (!$this->inputOptions['data-combo-field']) {
194 2
            $this->inputOptions['data-combo-field'] = $this->name;
195
        }
196 2
        if (!isset($this->inputOptions['unselect'])) {
197 2
            $this->inputOptions['unselect'] = null;
198
        }
199 2
    }
200
201
    public function run()
202
    {
203
        $this->registerClientConfig();
204
        $this->registerClientScript();
205
        return $this->renderInput();
206
    }
207
208
    /**
209
     * Renders text input that will be used by the plugin.
210
     * Must apply [[inputOptions]] to the HTML element.
211
     *
212
     * @param string $type for compatible reasons. Should not be used
213
     * @return string
214
     */
215
    protected function renderInput($type = null)
0 ignored issues
show
Unused Code introduced by
The parameter $type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
216
    {
217
        $html = [];
218
        if ($this->inputOptions['readonly']) {
219
            // As it was said in comments of `init` method, the `select` element does not support property `readonly`.
220
            // However, disabled select will not be submitted.
221
            // Solution: render hidden input to submit the attribue value.
222
            $html[] = Html::activeHiddenInput($this->model, $this->attribute, [
223
                'id' => $this->inputOptions['id'] . '-hidden',
224
            ]);
225
        }
226
        $html[] = Html::activeDropDownList($this->model, $this->attribute, $this->getCurrentOptions(), $this->inputOptions);
227
228
        return implode('', $html);
229
    }
230
231
    public function registerClientConfig()
232
    {
233
        $view = $this->view;
234
        ComboAsset::register($view);
235
236
        $pluginOptions = Json::encode($this->pluginOptions);
237
        $this->configId = md5($this->type . $pluginOptions);
238
        $view->registerJs("$.comboConfig().add('{$this->configId}', $pluginOptions);", View::POS_READY, 'combo_' . $this->configId);
239
    }
240
241
    public function registerClientScript()
242
    {
243
        $selector = $this->inputOptions['id'];
244
        $js = "$('#$selector').closest('{$this->formElementSelector}').combo().register('#$selector', '$this->configId');";
245
246
        $this->view->registerJs($js);
247
    }
248
249
    public function getReturn()
250
    {
251
        return $this->_return;
252
    }
253
254
    /**
255
     * @return mixed
256
     */
257 2
    public function getRename()
258
    {
259 2
        return $this->_rename;
260
    }
261
262
    /**
263
     * @return mixed
264
     */
265
    public function getFilter()
266
    {
267
        return $this->_filter;
268
    }
269
270
    /**
271
     * @param mixed $filter
272
     */
273
    public function setFilter($filter)
274
    {
275
        $this->_filter = $filter;
0 ignored issues
show
Documentation Bug introduced by
It seems like $filter of type * is incompatible with the declared type array of property $_filter.

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...
276
    }
277
278
    /**
279
     * @param mixed $rename
280
     */
281 2
    public function setRename($rename)
282
    {
283 2
        $this->_rename = $rename;
0 ignored issues
show
Documentation Bug introduced by
It seems like $rename of type * is incompatible with the declared type array of property $_rename.

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...
284 2
    }
285
286
    /**
287
     * @param mixed $return
288
     */
289 2
    public function setReturn($return)
290
    {
291 2
        $this->_return = $return;
292 2
    }
293
294
    /**
295
     * @return string
296
     * @see _primaryFilter
297
     */
298
    public function getPrimaryFilter()
299
    {
300
        return $this->_primaryFilter ?: $this->name . '_like';
301
    }
302
303
    /**
304
     * @param $primaryFilter
305
     * @see _primaryFilter
306
     */
307
    public function setPrimaryFilter($primaryFilter)
308
    {
309
        $this->_primaryFilter = $primaryFilter;
310
    }
311
312
    /**
313
     * Returns the config of the Combo, merges with the passed $config.
314
     *
315
     * @param array $options
316
     * @return array
317
     */
318
    public function getPluginOptions($options = [])
319
    {
320
        $defaultOptions = [
321
            'name' => $this->name,
322
            'type' => $this->type,
323
            'hasId' => $this->hasId,
324
            'select2Options' => [
325
                'width'       => '100%',
326
                'placeholder' => '----------',
327
                'minimumInputLength' => '0',
328
                'ajax'        => [
329
                    'url'    => Url::toRoute($this->url),
330
                    'type'   => 'post',
331
                    'return' => $this->return,
332
                    'rename' => $this->rename,
333
                    'filter' => $this->filter,
334
                    'data' => new JsExpression("
335
                        function (event) {
336
                            return $(this).data('field').createFilter($.extend(true, {
337
                                '{$this->primaryFilter}': {format: event.term}
338
                            }, event.filters || {}));
339
                        }
340
                    "),
341
                ],
342
            ],
343
        ];
344
345
        return ArrayHelper::merge($defaultOptions, $this->_pluginOptions, $options);
346
    }
347
348
    public function getFormIsBulk()
349
    {
350
        return preg_match("/^\[.*\].+$/", $this->attribute);
351
    }
352
353
    /**
354
     * @param array $pluginOptions
355
     */
356
    public function setPluginOptions($pluginOptions)
357
    {
358
        $this->_pluginOptions = $pluginOptions;
359
    }
360
361
    /**
362
     * @return bool|string
363
     */
364
    public function getHasId()
365
    {
366
        return $this->_hasId === null ? (substr($this->attribute, -3) === '_id') : $this->_hasId;
367
    }
368
369
    /**
370
     * @param bool|string $hasId
371
     */
372
    public function setHasId($hasId)
373
    {
374
        $this->_hasId = $hasId;
375
    }
376
377
    /**
378
     * Method collects list of options that will be rendered inside the `select` tag.
379
     * @return array
380
     */
381
    protected function getCurrentOptions()
382
    {
383
        $value = Html::getAttributeValue($this->model, $this->attribute);
384
385
        if (!isset($value) || empty($value)) {
386
            return [];
387
        }
388
389
        if (!empty($this->current)) {
390
            return $this->current;
391
        }
392
393
        if ($this->getHasId()) {
394
            if (!is_scalar($value)) {
395
                Yii::error('When Combo has ID, property $current must be set manually, or attribute value must be a scalar. Value ' . var_export($value, true) . ' is not a scalar.', __METHOD__);
396
                return [];
397
            }
398
399
            return [$value => $value];
400
        } else {
401
            if (is_array($value)) {
402
                return array_combine(array_values($value), array_values($value));
403
            }
404
405
            return [$value => $value];
406
        }
407
    }
408
}
409