Passed
Pull Request — master (#192)
by Alexander
05:47 queued 02:55
created

Checkbox::generateInput()   B

Complexity

Conditions 11
Paths 7

Size

Total Lines 44
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 11.0061

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 11
eloc 27
nc 7
nop 0
dl 0
loc 44
ccs 26
cts 27
cp 0.963
crap 11.0061
rs 7.3166
c 1
b 0
f 0

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
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Field;
6
7
use InvalidArgumentException;
8
use Stringable;
9
use Yiisoft\Form\Field\Base\InputField;
10
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassInterface;
11
use Yiisoft\Form\Field\Base\ValidationClass\ValidationClassTrait;
12
use Yiisoft\Html\Html;
13
14
use function is_bool;
15
use function is_object;
16
use function is_string;
17
18
/**
19
 * The input element with a type attribute whose value is "checkbox" represents a state or option that can be toggled.
20
 *
21
 * @link https://html.spec.whatwg.org/multipage/input.html#checkbox-state-(type=checkbox)
22
 */
23
final class Checkbox extends InputField implements ValidationClassInterface
24
{
25
    use ValidationClassTrait;
26
27
    private ?string $uncheckValue = '0';
28
    private bool $enclosedByLabel = true;
29
    private ?string $inputLabel = null;
30
    private array $inputLabelAttributes = [];
31
    private bool $inputLabelEncode = true;
32
    private ?string $inputValue = null;
33
34
    /**
35
     * @param bool|float|int|string|Stringable|null $value Value that corresponds to "unchecked" state of the input.
36
     */
37 8
    public function uncheckValue(bool|float|int|string|Stringable|null $value): self
38
    {
39 8
        $new = clone $this;
40 8
        $new->uncheckValue = $this->prepareValue($value);
41 8
        return $new;
42
    }
43
44
    /**
45
     * If the input should be enclosed by label.
46
     *
47
     * @param bool $value If the input should be en closed by label.
48
     */
49 5
    public function enclosedByLabel(bool $value): self
50
    {
51 5
        $new = clone $this;
52 5
        $new->enclosedByLabel = $value;
53 5
        return $new;
54
    }
55
56
    /**
57
     * Label displayed next to the checkbox.
58
     *
59
     * When this option is specified, the checkbox will be enclosed by a label tag.
60
     *
61
     * @param string|null $value
62
     *
63
     * @return self
64
     *
65
     * @link https://www.w3.org/TR/html52/sec-forms.html#the-label-element
66
     */
67 7
    public function inputLabel(?string $value): self
68
    {
69 7
        $new = clone $this;
70 7
        $new->inputLabel = $value;
71 7
        return $new;
72
    }
73
74
    /**
75
     * HTML attributes for the label tag.
76
     *
77
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
78
     */
79 2
    public function inputLabelAttributes(array $attributes): self
80
    {
81 2
        $new = clone $this;
82 2
        $new->inputLabelAttributes = $attributes;
83 2
        return $new;
84
    }
85
86 2
    public function inputLabelEncode(bool $encode): self
87
    {
88 2
        $new = clone $this;
89 2
        $new->inputLabelEncode = $encode;
90 2
        return $new;
91
    }
92
93 3
    public function inputValue(bool|float|int|string|Stringable|null $value): self
94
    {
95 3
        $new = clone $this;
96 3
        $new->inputValue = $this->prepareValue($value);
97 3
        return $new;
98
    }
99
100
    /**
101
     * @link https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-disabled
102
     */
103
    public function disabled(bool $disabled = true): self
104
    {
105
        $new = clone $this;
106
        $new->inputTagAttributes['disabled'] = $disabled;
107
        return $new;
108
    }
109
110
    /**
111
     * Identifies the element (or elements) that describes the object.
112
     *
113
     * @link https://w3c.github.io/aria/#aria-describedby
114
     */
115
    public function ariaDescribedBy(string $value): self
116
    {
117
        $new = clone $this;
118
        $new->inputTagAttributes['aria-describedby'] = $value;
119
        return $new;
120
    }
121
122
    /**
123
     * Defines a string value that labels the current element.
124
     *
125
     * @link https://w3c.github.io/aria/#aria-label
126
     */
127
    public function ariaLabel(string $value): self
128
    {
129
        $new = clone $this;
130
        $new->inputTagAttributes['aria-label'] = $value;
131
        return $new;
132
    }
133
134
    /**
135
     * Focus on the control (put cursor into it) when the page loads. Only one form element could be in focus
136
     * at the same time.
137
     *
138
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-fe-autofocus
139
     */
140
    public function autofocus(bool $value = true): self
141
    {
142
        $new = clone $this;
143
        $new->inputTagAttributes['autofocus'] = $value;
144
        return $new;
145
    }
146
147
    /**
148
     * The `tabindex` attribute indicates that its element can be focused, and where it participates in sequential
149
     * keyboard navigation (usually with the Tab key, hence the name).
150
     *
151
     * It accepts an integer as a value, with different results depending on the integer's value:
152
     *
153
     * - A negative value (usually `tabindex="-1"`) means that the element is not reachable via sequential keyboard
154
     *   navigation, but could be focused with Javascript or visually. It's mostly useful to create accessible widgets
155
     *   with JavaScript.
156
     * - `tabindex="0"` means that the element should be focusable in sequential keyboard navigation, but its order is
157
     *   defined by the document's source order.
158
     * - A positive value means the element should be focusable in sequential keyboard navigation, with its order
159
     *   defined by the value of the number. That is, `tabindex="4"` is focused before `tabindex="5"`, but after
160
     *   `tabindex="3"`.
161
     *
162
     * @link https://html.spec.whatwg.org/multipage/interaction.html#attr-tabindex
163
     */
164
    public function tabIndex(?int $value): self
165
    {
166
        $new = clone $this;
167
        $new->inputTagAttributes['tabindex'] = $value;
168
        return $new;
169
    }
170
171 20
    protected function generateInput(): string
172
    {
173 20
        $value = $this->getAttributeValue();
174
175 20
        if (!is_bool($value)
176 20
            && !is_string($value)
177 20
            && !is_numeric($value)
178 20
            && $value !== null
179 20
            && (!is_object($value) || !method_exists($value, '__toString'))
180
        ) {
181
            throw new InvalidArgumentException(
182
                'Checkbox widget requires a string, numeric, bool, Stringable or null value.'
183
            );
184
        }
185
186 20
        $value = $this->prepareValue($value);
187
188 20
        $tagAttributes = $this->getInputTagAttributes();
189
190 20
        $inputValue = $this->inputValue;
191 20
        $inputValue = $inputValue ?? $this->prepareValue($tagAttributes['value'] ?? null);
192 20
        unset($tagAttributes['value']);
193 20
        $inputValue = $inputValue ?? '1';
194
195 20
        $checkbox = Html::checkbox($this->getInputName(), $inputValue, $tagAttributes);
196
197 20
        $label = $this->inputLabel ?? $this->getAttributeLabel();
198
199 20
        if ($this->enclosedByLabel) {
200 16
            $checkbox = $checkbox
201 16
                ->label($label, $this->inputLabelAttributes)
202 16
                ->labelEncode($this->inputLabelEncode);
203
        }
204
205 20
        $html = $checkbox
206 20
            ->checked($inputValue === $value)
207 20
            ->uncheckValue($this->uncheckValue)
208 20
            ->render();
209
210 20
        if (!$this->enclosedByLabel && $this->inputLabel !== null) {
211 3
            $html .= ' ' . ($this->inputLabelEncode ? Html::encode($this->inputLabel) : $this->inputLabel);
212
        }
213
214 20
        return $html;
215
    }
216
217 18
    protected function shouldHideLabel(): bool
218
    {
219 18
        return $this->enclosedByLabel;
220
    }
221
222 21
    private function prepareValue(mixed $value): ?string
223
    {
224 21
        if ($value === null) {
225 19
            return null;
226
        }
227
228 20
        if (is_bool($value)) {
229 19
            return $value ? '1' : '0';
230
        }
231
232 6
        return (string) $value;
233
    }
234
235 20
    protected function prepareContainerTagAttributes(array &$attributes): void
236
    {
237 20
        if ($this->hasFormModelAndAttribute()) {
238 20
            $this->addValidationClassToTagAttributes(
239
                $attributes,
240 20
                $this->getFormModel(),
241 20
                $this->getAttributeName(),
242
            );
243
        }
244
    }
245
}
246