Test Failed
Pull Request — master (#160)
by Wilmer
02:22
created

RadioList::itemsFromValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 0
cts 0
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Widget;
6
7
use Closure;
8
use InvalidArgumentException;
9
use Stringable;
10
use Yiisoft\Form\Widget\Attribute\ChoiceAttributes;
11
use Yiisoft\Html\Widget\RadioList\RadioItem;
12
use Yiisoft\Html\Widget\RadioList\RadioList as RadioListTag;
13
14
/**
15
 * Generates a list of radio.
16
 */
17
final class RadioList extends ChoiceAttributes
18
{
19
    private array $containerAttributes = [];
20
    private ?string $containerTag = 'div';
21
    /** @psalm-var array[] */
22
    private array $individualItemsAttributes = [];
23
    /** @psalm-var array<array-key, string> */
24
    private array $items = [];
25
    private array $itemsAttributes = [];
26
    /** @psalm-var Closure(RadioItem):string|null */
27
    private ?Closure $itemsFormatter = null;
28
    /** @var bool[]|float[]|int[]|string[]|Stringable[] */
29
    private array $itemsFromValues = [];
30
    private string $separator = '';
31
    private ?string $uncheckValue = null;
32
33
    /**
34
     * Focus on the control (put cursor into it) when the page loads.
35
     * Only one form element could be in focus at the same time.
36
     *
37
     * @return static
38
     *
39
     * @link https://www.w3.org/TR/html52/sec-forms.html#autofocusing-a-form-control-the-autofocus-attribute
40
     *
41
     * @psalm-suppress MethodSignatureMismatch
42
     */
43
    public function autofocus(): self
44
    {
45
        $new = clone $this;
46 3
        $new->containerAttributes['autofocus'] = true;
47
        return $new;
48 3
    }
49 3
50 3
    /**
51
     * The container attributes for generating the list of checkboxes tag using {@see CheckBoxList}.
52
     *
53
     * @param array $value
54
     *
55
     * @return static
56
     *
57
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
58
     */
59
    public function containerAttributes(array $value): self
60 5
    {
61
        $new = clone $this;
62 5
        $new->containerAttributes = $value;
63 5
        return $new;
64 5
    }
65
66
    /**
67
     * The tag name for the container element.
68
     *
69
     * @param string|null $name tag name. if `null` disabled rendering.
70
     *
71
     * @return static
72
     */
73
    public function containerTag(?string $name = null): self
74
    {
75
        $new = clone $this;
76
        $new->containerTag = $name;
77
        return $new;
78
    }
79
80
    /**
81
     * Set the ID of container the widget.
82 3
     *
83
     * @param string|null $id
84 3
     *
85 3
     * @return static
86 3
     *
87
     * @link https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute
88
     *
89
     * @psalm-suppress MethodSignatureMismatch
90
     */
91
    public function id(?string $id): self
92
    {
93
        $new = clone $this;
94
        $new->containerAttributes['id'] = $id;
95
        return $new;
96
    }
97
98 3
    /**
99
     * The specified attributes for items.
100 3
     *
101 3
     * @param array $value
102 3
     *
103
     * @return static
104
     *
105
     * @psalm-param array[] $value
106
     */
107
    public function individualItemsAttributes(array $value = []): self
108
    {
109
        $new = clone $this;
110
        $new->individualItemsAttributes = $value;
111
        return $new;
112
    }
113
114
    /**
115
     * The data used to generate the list of radios.
116
     *
117
     * The array keys are the list of radio values, and the array values are the corresponding labels.
118 27
     *
119
     * Note that the labels will NOT be HTML-encoded, while the values will.
120 27
     *
121 27
     * @param array $value
122 27
     *
123
     * @return static
124
     *
125
     * @psalm-param array<array-key, string> $value
126
     */
127
    public function items(array $value = []): self
128
    {
129
        $new = clone $this;
130
        $new->items = $value;
131
        return $new;
132 3
    }
133
134 3
    /**
135 3
     * The attributes for generating the list of radio tag using {@see RadioList}.
136 3
     *
137
     * @param array $value
138
     *
139
     * @return static
140
     */
141
    public function itemsAttributes(array $value = []): self
142
    {
143
        $new = clone $this;
144
        $new->itemsAttributes = $value;
145
        return $new;
146
    }
147
148
    /**
149
     * Callable, a callback that can be used to customize the generation of the HTML code corresponding to a single
150
     * item in $items.
151
     *
152
     * The signature of this callback must be:
153
     *
154
     * ```php
155 3
     * function ($index, $label, $name, $checked, $value)
156
     * ```
157 3
     *
158 3
     * @param Closure|null $value
159 3
     *
160
     * @return static
161
     *
162
     * @psalm-param Closure(RadioItem):string|null $value
163
     */
164
    public function itemsFormatter(?Closure $value): self
165
    {
166
        $new = clone $this;
167
        $new->itemsFormatter = $value;
168
        return $new;
169
    }
170
171 16
    /**
172
     * The data used to generate the list of radios.
173 16
     *
174 16
     * The array keys are the list of radio values, and the array values are the corresponding labels.
175 16
     *
176
     * @param bool[]|float[]|int[]|string[]|Stringable[] $itemsFromValues
177
     *
178
     * @return static
179
     */
180
    public function itemsFromValues(array $itemsFromValues = []): self
181
    {
182
        $new = clone $this;
183
        $new->itemsFromValues = $itemsFromValues;
184
        return $new;
185
    }
186 3
187
    /**
188 3
     * The HTML code that separates items.
189 3
     *
190 3
     * @param string $value
191
     *
192
     * @return static
193
     */
194
    public function separator(string $value = ''): self
195
    {
196
        $new = clone $this;
197
        $new->separator = $value;
198
        return $new;
199
    }
200 3
201
    /**
202 3
     * The tabindex global attribute indicates that its element can be focused, and where it participates in sequential
203 3
     * keyboard navigation (usually with the Tab key, hence the name).
204 3
     *
205
     * It accepts an integer as a value, with different results depending on the integer's value:
206
     *
207
     * - A negative value (usually tabindex="-1") means that the element is not reachable via sequential keyboard
208
     * navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
209
     * with JavaScript.
210
     * - tabindex="0" means that the element should be focusable in sequential keyboard navigation, but its order is
211
     * defined by the document's source order.
212 3
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
213
     * defined by the value of the number. That is, tabindex="4" is focused before tabindex="5", but after tabindex="3".
214 3
     *
215 3
     * @param int $value
216 3
     *
217
     * @return static
218
     *
219
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
220
     *
221
     * @psalm-suppress MethodSignatureMismatch
222
     */
223
    public function tabIndex(int $value): self
224
    {
225
        $new = clone $this;
226 28
        $new->containerAttributes['tabindex'] = $value;
227
        return $new;
228 28
    }
229 28
230
    /**
231
     * @param bool|float|int|string|Stringable|null $value Value that corresponds to "unchecked" state of the input.
232 28
     *
233
     * @return static
234 28
     */
235 2
    public function uncheckValue($value): self
236
    {
237
        $new = clone $this;
238
        $new->uncheckValue = $value === null ? null : (string) $value;
239 26
        return $new;
240
    }
241
242 26
    /**
243
     * Generates a list of radio buttons.
244
     *
245 26
     * A radio button list is like a checkbox list, except that it only allows single selection.
246
     *
247 26
     * @return string the generated radio button list
248 24
     */
249 2
    protected function run(): string
250 2
    {
251
        /** @psalm-var array[] */
252
        [$attributes, $containerAttributes] = $this->buildList($this->attributes, $this->containerAttributes);
253 26
254 2
        /**
255
         * @var iterable<int, scalar|Stringable>|scalar|Stringable|null
256
         *
257
         * @link https://html.spec.whatwg.org/multipage/input.html#attr-input-value
258 26
         */
259 26
        $value = $attributes['value'] ?? $this->getAttributeValue();
260 26
        unset($attributes['value']);
261 26
262 26
        if (is_iterable($value) || is_object($value)) {
263 26
            throw new InvalidArgumentException('RadioList widget value can not be an iterable or an object.');
264 26
        }
265 26
266 26
        $name = $this->getInputName();
267
268
        /** @var string */
269
        if (!empty($attributes['name']) && is_string($attributes['name'])) {
270
            $name = $attributes['name'];
271
        }
272
273
        $radioList = RadioListTag::create($name);
274
275
        if ($this->items !== []) {
276
            $radioList = $radioList->items($this->items, $this->getEncode());
277
        } elseif ($this->itemsFromValues !== []) {
278
            $radioList = $radioList->itemsFromValues($this->itemsFromValues, $this->getEncode());
279
        }
280
281
        if ($this->separator !== '') {
282
            $radioList = $radioList->separator($this->separator);
283
        }
284
285
        if ($this->itemsAttributes !== []) {
286
            $radioList = $radioList->replaceRadioAttributes($this->itemsAttributes);
287
        }
288
289
        return $radioList
290
            ->containerAttributes($containerAttributes)
291
            ->containerTag($this->containerTag)
292
            ->individualInputAttributes($this->individualItemsAttributes)
293
            ->itemFormatter($this->itemsFormatter)
294
            ->radioAttributes($attributes)
295
            ->uncheckValue($this->uncheckValue)
296
            ->value(is_bool($value) ? (int) $value : $value)
297
            ->render();
298
    }
299
}
300