Passed
Pull Request — master (#151)
by Wilmer
02:19
created

FieldAttributes::generateId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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