Passed
Push — dependabot/github_actions/acti... ( 6c9f07 )
by
unknown
14:42 queued 10:29
created

ListInput::config()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 3
dl 0
loc 7
ccs 6
cts 6
cp 1
crap 1
rs 10
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