Passed
Push — master ( be9edc...28f389 )
by Sergei
02:42
created

CheckboxList::addCheckboxLabelAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 5
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Html\Widget\CheckboxList;
6
7
use Closure;
8
use Stringable;
9
use Yiisoft\Arrays\ArrayHelper;
10
use Yiisoft\Html\Html;
11
use Yiisoft\Html\NoEncodeStringableInterface;
12
use Yiisoft\Html\Tag\Input;
13
14
use function is_array;
15
16
/**
17
 * `CheckboxList` represents a list of checkboxes and their corresponding labels.
18
 */
19
final class CheckboxList implements NoEncodeStringableInterface
20
{
21
    private ?string $containerTag = 'div';
22
    private array $containerAttributes = [];
23
    private array $checkboxAttributes = [];
24
    private array $checkboxLabelAttributes = [];
25
26
    /**
27
     * @var array[]
28
     */
29
    private array $individualInputAttributes = [];
30
31
    private ?string $uncheckValue = null;
32
    private string $separator = "\n";
33
    private bool $encodeLabels = true;
34
35
    /**
36
     * @var string[]
37
     */
38
    private array $items = [];
39
40
    /**
41
     * @psalm-var list<string>
42
     */
43
    private array $values = [];
44
45
    /**
46
     * @psalm-var Closure(CheckboxItem):string|null
47
     */
48
    private ?Closure $itemFormatter = null;
49 43
50
    private function __construct(
51
        private string $name
52
    ) {
53
    }
54 43
55
    public static function create(string $name): self
56 43
    {
57
        return new self($name);
58
    }
59 2
60
    public function name(string $name): self
61 2
    {
62 2
        $new = clone $this;
63 2
        $new->name = $name;
64
        return $new;
65
    }
66 17
67
    public function withoutContainer(): self
68 17
    {
69 17
        $new = clone $this;
70 17
        $new->containerTag = null;
71
        return $new;
72
    }
73 4
74
    public function containerTag(?string $name): self
75 4
    {
76 4
        $new = clone $this;
77 4
        $new->containerTag = $name;
78
        return $new;
79
    }
80 4
81
    public function containerAttributes(array $attributes): self
82 4
    {
83 4
        $new = clone $this;
84 4
        $new->containerAttributes = $attributes;
85
        return $new;
86
    }
87 2
88
    public function addCheckboxAttributes(array $attributes): self
89 2
    {
90 2
        $new = clone $this;
91 2
        $new->checkboxAttributes = array_merge($new->checkboxAttributes, $attributes);
92
        return $new;
93
    }
94 7
95
    public function checkboxAttributes(array $attributes): self
96 7
    {
97 7
        $new = clone $this;
98 7
        $new->checkboxAttributes = $attributes;
99
        return $new;
100
    }
101
102
    public function addCheckboxLabelAttributes(array $attributes): self
103
    {
104 4
        $new = clone $this;
105
        $new->checkboxLabelAttributes = array_merge($new->checkboxLabelAttributes, $attributes);
106 4
        return $new;
107 4
    }
108 4
109
    public function checkboxLabelAttributes(array $attributes): self
110
    {
111
        $new = clone $this;
112
        $new->checkboxLabelAttributes = $attributes;
113
        return $new;
114 3
    }
115
116 3
    /**
117 3
     * @param array[] $attributes
118 3
     */
119
    public function addIndividualInputAttributes(array $attributes): self
120
    {
121
        $new = clone $this;
122
        $new->individualInputAttributes = array_replace($new->individualInputAttributes, $attributes);
123
        return $new;
124
    }
125 42
126
    /**
127 42
     * @param array[] $attributes
128 42
     */
129 42
    public function individualInputAttributes(array $attributes): self
130 42
    {
131
        $new = clone $this;
132
        $new->individualInputAttributes = $attributes;
133
        return $new;
134
    }
135
136
    /**
137
     * @param string[] $items
138
     * @param bool $encodeLabels Whether labels should be encoded.
139 5
     */
140
    public function items(array $items, bool $encodeLabels = true): self
141 5
    {
142
        $new = clone $this;
143 5
        $new->items = $items;
144 5
        $new->encodeLabels = $encodeLabels;
145
        return $new;
146
    }
147
148
    /**
149 10
     * Fills items from an array provided. Array values are used for both input labels and input values.
150
     *
151 10
     * @param bool[]|float[]|int[]|string[]|Stringable[] $values
152 10
     * @param bool $encodeLabels Whether labels should be encoded.
153 10
     */
154
    public function itemsFromValues(array $values, bool $encodeLabels = true): self
155
    {
156
        $values = array_map('\strval', $values);
157
158
        return $this->items(
159 8
            array_combine($values, $values),
160
            $encodeLabels
161
        );
162 8
    }
163
164 8
    public function value(bool|string|int|float|Stringable ...$value): self
165
    {
166
        $new = clone $this;
167
        $new->values = array_map('\strval', array_values($value));
168
        return $new;
169
    }
170 5
171
    /**
172 5
     * @psalm-param iterable<int, Stringable|scalar> $values
173 5
     */
174 5
    public function values(iterable $values): self
175
    {
176
        /** @psalm-var iterable<int, Stringable|scalar> $values */
177
        $values = is_array($values) ? $values : iterator_to_array($values);
178
179
        return $this->value(...$values);
180 4
    }
181
182 4
    /**
183 4
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-formelements-form
184 4
     */
185
    public function form(?string $formId): self
186
    {
187
        $new = clone $this;
188
        $new->checkboxAttributes['form'] = $formId;
189
        return $new;
190 3
    }
191
192 3
    /**
193 3
     * @link https://www.w3.org/TR/html52/sec-forms.html#the-readonly-attribute
194 3
     */
195
    public function readonly(bool $readonly = true): self
196
    {
197 10
        $new = clone $this;
198
        $new->checkboxAttributes['readonly'] = $readonly;
199 10
        return $new;
200 10
    }
201 10
202
    /**
203
     * @link https://www.w3.org/TR/html52/sec-forms.html#element-attrdef-disabledformelements-disabled
204 3
     */
205
    public function disabled(bool $disabled = true): self
206 3
    {
207 3
        $new = clone $this;
208 3
        $new->checkboxAttributes['disabled'] = $disabled;
209
        return $new;
210
    }
211
212
    public function uncheckValue(bool|float|int|string|Stringable|null $value): self
213
    {
214 2
        $new = clone $this;
215
        $new->uncheckValue = $value === null ? null : (string)$value;
216 2
        return $new;
217 2
    }
218 2
219
    public function separator(string $separator): self
220
    {
221 42
        $new = clone $this;
222
        $new->separator = $separator;
223 42
        return $new;
224
    }
225 42
226 42
    /**
227 42
     * @psalm-param Closure(CheckboxItem):string|null $formatter
228 39
     */
229
    public function itemFormatter(?Closure $formatter): self
230
    {
231
        $new = clone $this;
232 39
        $new->itemFormatter = $formatter;
233 39
        return $new;
234 39
    }
235 39
236
    public function render(): string
237
    {
238
        $name = Html::getArrayableName($this->name);
239 39
240
        $lines = [];
241 39
        $index = 0;
242 39
        foreach ($this->items as $value => $label) {
243
            $item = new CheckboxItem(
244
                $index,
245 42
                $name,
246 42
                $value,
247 7
                ArrayHelper::isIn($value, $this->values),
248
                array_merge(
249 42
                    $this->checkboxAttributes,
250 24
                    $this->individualInputAttributes[$value] ?? [],
251
                    ['name' => $name, 'value' => $value]
252 42
                ),
253 39
                $label,
254
                $this->encodeLabels,
255 42
                $this->checkboxLabelAttributes,
256 24
            );
257
            $lines[] = $this->formatItem($item);
258
            $index++;
259 42
        }
260
261
        $html = [];
262 7
        if ($this->uncheckValue !== null) {
263
            $html[] = $this->renderUncheckInput();
264
        }
265 7
        if (!empty($this->containerTag)) {
266 7
            $html[] = Html::openTag($this->containerTag, $this->containerAttributes);
267 7
        }
268
        if ($lines) {
269 7
            $html[] = implode($this->separator, $lines);
270 7
        }
271
        if (!empty($this->containerTag)) {
272
            $html[] = Html::closeTag($this->containerTag);
273 7
        }
274 7
275
        return implode("\n", $html);
276 7
    }
277
278
    private function renderUncheckInput(): string
279 7
    {
280
        return
281
            Input::hidden(
282 39
                Html::getNonArrayableName($this->name),
283
                $this->uncheckValue
284 39
            )
285 1
                ->addAttributes(
286
                    array_merge(
287
                        [
288 38
                            // Make sure disabled input is not sending any value
289 38
                            'disabled' => $this->checkboxAttributes['disabled'] ?? null,
290 38
                            'form' => $this->checkboxAttributes['form'] ?? null,
291 38
                        ],
292
                        $this->individualInputAttributes[$this->uncheckValue] ?? []
293 38
                    )
294
                )
295
                ->render();
296 1
    }
297
298 1
    private function formatItem(CheckboxItem $item): string
299
    {
300
        if ($this->itemFormatter !== null) {
301
            return ($this->itemFormatter)($item);
302
        }
303
304
        $checkbox = Html::checkbox($item->name, $item->value, $item->checkboxAttributes)
305
            ->checked($item->checked)
306
            ->label($item->label, $item->labelAttributes)
307
            ->labelEncode($item->encodeLabel);
308
309
        return $checkbox->render();
310
    }
311
312
    public function __toString(): string
313
    {
314
        return $this->render();
315
    }
316
}
317