Passed
Pull Request — master (#157)
by
unknown
03:10
created

FieldAttributes::widgetTemplate()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2.0185

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 2
dl 0
loc 11
ccs 5
cts 6
cp 0.8333
crap 2.0185
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Widget\Attribute;
6
7
use InvalidArgumentException;
8
use Yiisoft\Form\FormModelInterface;
9
use Yiisoft\Form\Helper\HtmlForm;
10
use Yiisoft\Form\Helper\HtmlFormErrors;
11
use Yiisoft\Html\Html;
12
use Yiisoft\Validator\Rule\HasLength;
13
use Yiisoft\Validator\Rule\MatchRegularExpression;
14
use Yiisoft\Validator\Rule\Number;
15
use Yiisoft\Validator\Rule\Required;
16
use Yiisoft\Validator\Rule\Url;
17
18
trait FieldAttributes
19
{
20
    private bool $ariaDescribedBy = false;
21
    private string $attribute = '';
22
    private array $attributes = [];
23
    private string $containerClass = '';
24
    private string $errorClass = '';
25
    private string $errorMessage = '';
26
    private string $hintClass = '';
27
    private string $id = '';
28
    private string $inputClass = '';
29
    private string $labelClass = '';
30
    private string $invalidClass = '';
31
    private string $validClass = '';
32
    private array $parts = [];
33
    private string $template = "{label}\n{input}\n{hint}\n{error}";
34
    private array $widgetTemplates = [];
35
    private string $validationStateOn = 'input';
36
    private ?FormModelInterface $formModel = null;
37
38
    /**
39
     * Set aria-describedby attribute.
40
     *
41
     * @return static
42
     *
43
     * @link https://www.w3.org/TR/WCAG20-TECHS/ARIA1.html
44
     */
45 2
    public function ariaDescribedBy(): self
46
    {
47 2
        $new = clone $this;
48 2
        $new->ariaDescribedBy = true;
49 2
        return $new;
50
    }
51
52
    /**
53
     * Set form interface, attribute name and attributes, and attributes for the widget.
54
     *
55
     * @param FormModelInterface $formModel Form.
56
     * @param string $attribute Form model property this widget is rendered for.
57
     * @param array $attributes The HTML attributes for the widget container tag.
58
     *
59
     * @return static
60
     *
61
     * {@see \Yiisoft\Html\Html::renderTagAttributes()} for details on how attributes are being rendered.
62
     */
63 176
    public function config(FormModelInterface $formModel, string $attribute, array $attributes = []): self
64
    {
65 176
        $new = clone $this;
66 176
        $new->formModel = $formModel;
67 176
        $new->attribute = $attribute;
68 176
        $new->attributes = $attributes;
69 176
        return $new;
70
    }
71
72
    /**
73
     * Set container css class.
74
     *
75
     * @return static
76
     */
77 2
    public function containerClass(string $value): self
78
    {
79 2
        $new = clone $this;
80 2
        $new->containerClass = $value;
81 2
        return $new;
82
    }
83
84
    /**
85
     * Set error css class.
86
     *
87
     * @return static
88
     */
89 9
    public function errorClass(string $value): self
90
    {
91 9
        $new = clone $this;
92 9
        $new->errorClass = $value;
93 9
        return $new;
94
    }
95
96
    /**
97
     * Set hint css class.
98
     *
99
     * @return static
100
     */
101 9
    public function hintClass(string $value): self
102
    {
103 9
        $new = clone $this;
104 9
        $new->hintClass = $value;
105 9
        return $new;
106
    }
107
108
    /**
109
     * Set input css class.
110
     *
111
     * @return static
112
     */
113 2
    public function inputClass(string $value): self
114
    {
115 2
        $new = clone $this;
116 2
        $new->inputClass = $value;
117 2
        return $new;
118
    }
119
120
    /**
121
     * Set invalid css class.
122
     *
123
     * @return static
124
     */
125 9
    public function invalidClass(string $value): self
126
    {
127 9
        $new = clone $this;
128 9
        $new->invalidClass = $value;
129 9
        return $new;
130
    }
131
132
    /**
133
     * Set the label css class.
134
     *
135
     * @return static
136
     */
137 2
    public function labelClass(string $value): self
138
    {
139 2
        $new = clone $this;
140 2
        $new->labelClass = $value;
141 2
        return $new;
142
    }
143
144
    /**
145
     * Set layout template for render a field.
146
     *
147
     * @param string $value
148
     *
149
     * @return static
150
     */
151 4
    public function template(string $value): self
152
    {
153 4
        $new = clone $this;
154 4
        $new->template = $value;
155 4
        return $new;
156
    }
157
158
    /**
159
     * Set layout templates for each widget
160
     *
161
     * @param array $templates
162
     * @param bool $replace
163
     *
164
     * @return static
165
     */
166 2
    public function widgetTemplates(array $templates, bool $replace = false): self
167
    {
168 2
        $new = clone $this;
169
170 2
        if ($replace) {
171
            $new->widgetTemplates = $templates;
172
        } else {
173 2
            $new->widgetTemplates = array_merge($new->widgetTemplates, $templates);
174
        }
175
176 2
        return $new;
177
    }
178
179
    /**
180
     * Set layout template for current widget
181
     *
182
     * @param string $widgetClass
183
     * @param string|null $template
184
     *
185
     * @return static
186
     */
187 1
    public function widgetTemplate(string $widgetClass, ?string $template): self
188
    {
189 1
        $new = clone $this;
190
191 1
        if (empty($template)) {
192 1
            unset($new->widgetTemplates[$widgetClass]);
193
        } else {
194
            $new->widgetTemplates[$widgetClass] = $template;
195
        }
196
197 1
        return $new;
198
    }
199
200
    /**
201
     * Set the value valid css class.
202
     *
203
     * @param string $value is the valid css class.
204
     *
205
     * @return static
206
     */
207 9
    public function validClass(string $value): self
208
    {
209 9
        $new = clone $this;
210 9
        $new->validClass = $value;
211 9
        return $new;
212
    }
213
214 176
    protected function getFormModel(): FormModelInterface
215
    {
216 176
        if ($this->formModel === null) {
217 1
            throw new InvalidArgumentException('Form model is not set.');
218
        }
219
220 175
        return $this->formModel;
221
    }
222
223 175
    private function addValidatorAttributeHtml(
224
        FormModelInterface $formModel,
225
        string $attribute,
226
        array $attributes,
227
        string $type
228
    ): array {
229
        /** @var array */
230 175
        $rules = $formModel->getRules()[$attribute] ?? [];
231
232
        /** @var object $rule */
233 175
        foreach ($rules as $rule) {
234 20
            if ($rule instanceof Required) {
235 14
                $attributes['required'] = true;
236
            }
237 20
            if ($rule instanceof HasLength && in_array($type, self::HAS_LENGTH_TYPES, true)) {
238
                /** @var string */
239 11
                $attributes['maxlength'] = $rule->getOptions()['max'];
240
                /** @var string */
241 11
                $attributes['minlength'] = $rule->getOptions()['min'];
242
            }
243 20
            if ($rule instanceof MatchRegularExpression && in_array($type, self::MATCH_REGULAR_EXPRESSION_TYPES, true)) {
244
                /** @var string */
245 5
                $pattern = $rule->getOptions()['pattern'];
246 5
                $attributes['pattern'] = Html::normalizeRegexpPattern($pattern);
247
            }
248 20
            if ($rule instanceof Number && $type === self::TYPE_NUMBER) {
249
                /** @var string */
250 2
                $attributes['max'] = $rule->getOptions()['max'];
251
                /** @var string */
252 2
                $attributes['min'] = $rule->getOptions()['min'];
253
            }
254 20
            if ($rule instanceof Url && $type === self::TYPE_URL) {
255
                /** @var array<array-key, string> */
256 1
                $validSchemes = $rule->getOptions()['validSchemes'];
257
258 1
                $schemes = [];
259 1
                foreach ($validSchemes as $scheme) {
260 1
                    $schemes[] = $this->getSchemePattern($scheme);
261
                }
262
263
                /** @var array<array-key, float|int|string>|string */
264 1
                $pattern = $rule->getOptions()['pattern'];
265 1
                $normalizePattern = str_replace('{schemes}', '(' . implode('|', $schemes) . ')', $pattern);
266 1
                $attributes['pattern'] = Html::normalizeRegexpPattern($normalizePattern);
267
            }
268
        }
269
270 175
        return $attributes;
271
    }
272
273 1
    private function getSchemePattern(string $scheme): string
274
    {
275 1
        $result = '';
276 1
        for ($i = 0, $length = mb_strlen($scheme); $i < $length; $i++) {
277 1
            $result .= '[' . mb_strtolower($scheme[$i]) . mb_strtoupper($scheme[$i]) . ']';
278
        }
279 1
        return $result;
280
    }
281
282
    /**
283
     * Return the input id.
284
     *
285
     * @return string
286
     */
287 1
    private function getId(): string
288
    {
289
        /** @var string */
290 1
        $id = $this->attributes['id'] ?? $this->id;
291
292 1
        return $id === '' ? HtmlForm::getInputId($this->getFormModel(), $this->attribute) : $id;
293
    }
294
295 175
    private function setInputAttributes(array $attributes): array
296
    {
297 175
        $new = clone $this;
298 175
        $placeHolder = '';
299
        /** @var string */
300 175
        $type = $attributes['type'] ?? '';
301 175
        unset($attributes['type']);
302 175
        $attributes = $new->addValidatorAttributeHtml($new->getFormModel(), $new->attribute, $attributes, $type);
303 175
        $attributeName = HtmlForm::getAttributeName($new->getFormModel(), $new->attribute);
304
305 175
        if ($new->ariaDescribedBy === true) {
306 1
            $attributes['aria-describedby'] = $new->getId();
307
        }
308
309 175
        if ($new->inputClass !== '') {
310 1
            Html::addCssClass($attributes, $new->inputClass);
311
        }
312
313 175
        if ($new->errorClass !== '' && HtmlFormErrors::hasErrors($new->getFormModel(), $attributeName)) {
314 8
            Html::addCssClass($attributes, $new->invalidClass);
315 175
        } elseif ($new->validClass !== '' && $new->getFormModel()->isValidated()) {
316 8
            Html::addCssClass($attributes, $new->validClass);
317
        }
318
319 175
        if (!in_array($type, self::NO_PLACEHOLDER_TYPES, true)) {
320 145
            $placeHolder = $new->getFormModel()->getAttributePlaceholder($new->attribute);
321
        }
322
323 175
        if (!isset($attributes['placeholder']) && $placeHolder !== '') {
324 59
            $attributes['placeholder'] = $placeHolder;
325
        }
326
327 175
        return $attributes;
328
    }
329
}
330