Passed
Push — dependabot/github_actions/shiv... ( a0bd3e )
by
unknown
10:33
created

ListInput::run()   F

Complexity

Conditions 17
Paths 300

Size

Total Lines 117
Code Lines 86

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 84
CRAP Score 17.0036

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 17
eloc 86
c 2
b 0
f 0
nc 300
nop 0
dl 0
loc 117
ccs 84
cts 86
cp 0.9767
crap 17.0036
rs 2.8121

How to fix   Long Method    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
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Widget;
6
7
use Closure;
8
use RuntimeException;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Form\FormModelInterface;
11
use Yiisoft\Form\Helper\HtmlForm;
12
use Yiisoft\Html\Html;
13
use Yiisoft\Html\Widget\CheckboxList\CheckboxItem;
14
use Yiisoft\Html\Widget\RadioList\RadioItem;
15
use Yiisoft\Widget\Widget;
16
17
final class ListInput extends Widget
18
{
19
    private ?string $id = null;
20
    private FormModelInterface $data;
21
    private string $attribute;
22
    private array $options = [];
23
    private string $charset = 'UTF-8';
24
    private array $items = [];
25
    private bool $noUnselect = false;
26
    private string $unselect = '';
27
    private string $type;
28
29
    /**
30
     * @psalm-var Closure(CheckboxItem):string|Closure(RadioItem):string|null
31
     */
32
    private ?Closure $itemFormatter = null;
33
34
    /**
35
     * Generates a list of input fields.
36
     *
37
     * This method is mainly called by {@see ListBox()}, {@see RadioList()} and {@see CheckboxList()}.
38
     *
39
     * @return string the generated input list
40
     */
41 59
    public function run(): string
42
    {
43 59
        $new = clone $this;
44 59
        $type = strtolower($new->type);
45
46 59
        $uncheckValue = ArrayHelper::remove($new->options, 'unselect');
47 59
        if (!$new->noUnselect) {
48 56
            $uncheckValue ??= $new->unselect;
49
        }
50
51 59
        if (!empty($new->getId())) {
52 59
            $new->options['id'] = $new->getId();
53
        }
54
55 59
        $containerTag = ArrayHelper::remove($new->options, 'tag', 'div');
56 59
        $separator = ArrayHelper::remove($new->options, 'separator', "\n");
57 59
        $encode = ArrayHelper::remove($new->options, 'encode', true);
58 59
        $disabled = ArrayHelper::remove($new->options, 'disabled', false);
59
60 59
        switch ($type) {
61 59
            case 'checkboxlist':
62 14
                $checkboxAttributes = ArrayHelper::remove($new->options, 'itemOptions', []);
63 14
                $encodeLabels = ArrayHelper::remove($checkboxAttributes, 'encode', true);
64
65
                /** @psalm-var Closure(CheckboxItem):string|null $itemFormatter */
66 14
                $itemFormatter = $this->itemFormatter;
67
68 14
                $value = $new->getValue();
69
                /** @psalm-suppress PossiblyInvalidArgument */
70 14
                return Html::checkboxList($new->getName())
71 14
                    ->values(!is_iterable($value) ? [$value] : $value)
72 14
                    ->uncheckValue($uncheckValue)
73 14
                    ->items($new->items, $encodeLabels)
74 14
                    ->itemFormatter($itemFormatter)
75 14
                    ->separator($separator)
76 14
                    ->containerTag($containerTag)
77 14
                    ->containerAttributes($new->options)
78 14
                    ->checkboxAttributes($checkboxAttributes)
79 14
                    ->disabled($disabled)
80 14
                    ->render();
81
82 46
            case 'radiolist':
83 12
                $radioAttributes = ArrayHelper::remove($new->options, 'itemOptions', []);
84 12
                $encodeLabels = ArrayHelper::remove($radioAttributes, 'encode', true);
85
86
                /** @psalm-var Closure(RadioItem):string|null $itemFormatter */
87 12
                $itemFormatter = $this->itemFormatter;
88
89 12
                $value = $new->getValue();
90 12
                return Html::radioList($new->getName())
91 12
                    ->value($value)
92 12
                    ->uncheckValue($uncheckValue)
93 12
                    ->items($new->items, $encodeLabels)
94 12
                    ->itemFormatter($itemFormatter)
95 12
                    ->separator($separator)
96 12
                    ->containerTag($containerTag)
97 12
                    ->containerAttributes($new->options)
98 12
                    ->radioAttributes($radioAttributes)
99 12
                    ->disabled($disabled)
100 12
                    ->render();
101
102 34
            case 'listbox':
103 9
            case 'dropdownlist':
104 34
                $groups = ArrayHelper::remove($new->options, 'groups', []);
105 34
                $optionsAttributes = ArrayHelper::remove($new->options, 'options', []);
106
107 34
                $items = [];
108 34
                foreach ($new->items as $value => $content) {
109 32
                    if (is_array($content)) {
110 3
                        $groupAttrs = $groups[$value] ?? [];
111 3
                        $groupAttrs['encode'] = false;
112 3
                        if (!isset($groupAttrs['label'])) {
113
                            $groupAttrs['label'] = $value;
114
                        }
115 3
                        $options = [];
116 3
                        foreach ($content as $v => $c) {
117 3
                            $options[] = Html::option($c, $v)
118 3
                                ->attributes($optionsAttributes[$v] ?? [])
119 3
                                ->encode($encode);
120
                        }
121 3
                        $items[] = Html::optgroup()
122 3
                            ->options(...$options)
123 3
                            ->attributes($groupAttrs);
124
                    } else {
125 29
                        $items[] = Html::option($content, $value)
126 29
                            ->attributes($optionsAttributes[$value] ?? [])
127 29
                            ->encode($encode);
128
                    }
129
                }
130
131 34
                $promptOption = null;
132 34
                $prompt = ArrayHelper::remove($new->options, 'prompt');
133 34
                if ($prompt) {
134 6
                    $promptText = $prompt['text'] ?? '';
135 6
                    if ($promptText) {
136 6
                        $promptOption = Html::option($promptText)
137 6
                            ->attributes($prompt['options'] ?? []);
138
                    }
139
                }
140
141 34
                if ($type === 'listbox') {
142 25
                    $new->options['size'] ??= 4;
143
                }
144
145 34
                $value = $new->getValue();
146
                /** @psalm-suppress PossiblyInvalidArgument */
147 34
                return Html::select($new->getName())
148 34
                    ->values(!is_iterable($value) ? [$value] : $value)
149 34
                    ->unselectValue($type === 'listbox' ? $uncheckValue : null)
150 34
                    ->promptOption($promptOption)
151 34
                    ->items(...$items)
152 34
                    ->attributes($new->options)
153 34
                    ->disabled($disabled)
154 34
                    ->render();
155
        }
156
157
        throw new RuntimeException('Unknown type: ' . $type);
158
    }
159
160
    /**
161
     * Set form model, name and options for the widget.
162
     *
163
     * @param FormModelInterface $data Form model.
164
     * @param string $attribute Form model property this widget is rendered for.
165
     * @param array $options The HTML attributes for the widget container tag.
166
     * See {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
167
     *
168
     * @return self
169
     */
170 59
    public function config(FormModelInterface $data, string $attribute, array $options = []): self
171
    {
172 59
        $new = clone $this;
173 59
        $new->data = $data;
174 59
        $new->attribute = $attribute;
175 59
        $new->options = $options;
176 59
        return $new;
177
    }
178
179
    /**
180
     * Set the character set used to generate the widget id. See {@see HtmlForm::getInputId()}.
181
     *
182
     * @param string $value
183
     *
184
     * @return self
185
     */
186
    public function charset(string $value): self
187
    {
188
        $new = clone $this;
189
        $new->charset = $value;
190
        return $new;
191
    }
192
193
    /**
194
     * Set the Id of the widget.
195
     *
196
     * @param string|null $value
197
     *
198
     * @return self
199
     */
200
    public function id(?string $value): self
201
    {
202
        $new = clone $this;
203
        $new->id = $value;
204
        return $new;
205
    }
206
207
    /**
208
     * Callable, a callback that can be used to customize the generation of the HTML code corresponding to a single
209
     * item in $items.
210
     *
211
     * The signature of this callback must be:
212
     *
213
     * ```php
214
     * function ($index, $label, $name, $checked, $value)
215
     * ```
216
     *
217
     * @param Closure|null $formatter
218
     *
219
     * @return self
220
     */
221 26
    public function item(?Closure $formatter): self
222
    {
223 26
        $new = clone $this;
224 26
        $new->itemFormatter = $formatter;
225 26
        return $new;
226
    }
227
228
    /**
229
     * The option data items.
230
     *
231
     * The array keys are option values, and the array values are the corresponding option labels. The array can also
232
     * be nested (i.e. some array values are arrays too). For each sub-array, an option group will be generated whose
233
     * label is the key associated with the sub-array. If you have a list of data {@see FormModel}, you may convert
234
     * them into the format described above using {@see \Yiisoft\Arrays\ArrayHelper::map()}
235
     *
236
     * Example:
237
     * ```php
238
     * [
239
     *     '1' => 'Santiago',
240
     *     '2' => 'Concepcion',
241
     *     '3' => 'Chillan',
242
     *     '4' => 'Moscu'
243
     *     '5' => 'San Petersburgo',
244
     *     '6' => 'Novosibirsk',
245
     *     '7' => 'Ekaterinburgo'
246
     * ];
247
     * ```
248
     *
249
     * Example with options groups:
250
     * ```php
251
     * [
252
     *     '1' => [
253
     *         '1' => 'Santiago',
254
     *         '2' => 'Concepcion',
255
     *         '3' => 'Chillan',
256
     *     ],
257
     *     '2' => [
258
     *         '4' => 'Moscu',
259
     *         '5' => 'San Petersburgo',
260
     *         '6' => 'Novosibirsk',
261
     *         '7' => 'Ekaterinburgo'
262
     *     ],
263
     * ];
264
     * ```
265
     *
266
     * @param array $value
267
     *
268
     * @return self
269
     */
270 59
    public function items(array $value): self
271
    {
272 59
        $new = clone $this;
273 59
        $new->items = $value;
274 59
        return $new;
275
    }
276
277
    /**
278
     * Allows you to disable the widgets hidden input tag.
279
     *
280
     * @param bool $value
281
     *
282
     * @return self
283
     */
284 59
    public function noUnselect(bool $value = true): self
285
    {
286 59
        $new = clone $this;
287 59
        $new->noUnselect = $value;
288 59
        return $new;
289
    }
290
291
    /**
292
     * Type of the input control to use.
293
     *
294
     * @param string $value
295
     *
296
     * @return self
297
     */
298 59
    public function type(string $value): self
299
    {
300 59
        $new = clone $this;
301 59
        $new->type = $value;
302 59
        return $new;
303
    }
304
305
    /**
306
     * The value that should be submitted when none of the listbox is selected.
307
     *
308
     * You may set this option to be null to prevent default value submission. If this option is not set, an empty
309
     * string will be submitted.
310
     *
311
     * @param string $value
312
     *
313
     * @return self
314
     */
315 47
    public function unselect(string $value = ''): self
316
    {
317 47
        $new = clone $this;
318 47
        $new->unselect = $value;
319 47
        return $new;
320
    }
321
322 59
    private function getId(): string
323
    {
324 59
        $id = $this->options['id'] ?? $this->id;
325
326 59
        if ($id === null) {
327 59
            $id = HtmlForm::getInputId($this->data, $this->attribute, $this->charset);
328
        }
329
330 59
        return $id !== false ? (string)$id : '';
331
    }
332
333 59
    private function getName(): string
334
    {
335 59
        return ArrayHelper::remove($this->options, 'name', HtmlForm::getInputName($this->data, $this->attribute));
336
    }
337
338 59
    private function getValue()
339
    {
340 59
        $value = HtmlForm::getAttributeValue($this->data, $this->attribute);
341
342 59
        if ($value !== null && is_scalar($value)) {
343 52
            $value = (string)$value;
344
        }
345
346 59
        return ArrayHelper::remove(
347 59
            $this->options,
348 59
            'value',
349 59
            $value
350
        );
351
    }
352
}
353