Passed
Pull Request — master (#171)
by Wilmer
02:40
created

Field::buildField()   B

Complexity

Conditions 10
Paths 24

Size

Total Lines 40
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 10

Importance

Changes 0
Metric Value
cc 10
eloc 18
nc 24
nop 0
dl 0
loc 40
ccs 19
cts 19
cp 1
crap 10
rs 7.6666
c 0
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\Widget;
6
7
use Yiisoft\Arrays\ArrayHelper;
8
use Yiisoft\Definitions\Exception\CircularReferenceException;
9
use Yiisoft\Definitions\Exception\InvalidConfigException;
10
use Yiisoft\Definitions\Exception\NotInstantiableException;
11
use Yiisoft\Factory\NotFoundException;
12
use Yiisoft\Form\FormModelInterface;
13
use Yiisoft\Form\Widget\Attribute\ButtonAttributes;
14
use Yiisoft\Form\Widget\Attribute\FieldAttributes;
15
use Yiisoft\Form\Widget\Attribute\InputAttributes;
16
use Yiisoft\Form\Widget\Attribute\GlobalAttributes;
17
use Yiisoft\Form\Widget\Attribute\PlaceholderInterface;
18
use Yiisoft\Form\Widget\Attribute\WidgetAttributes;
19
use Yiisoft\Form\Widget\FieldPart\Error;
20
use Yiisoft\Form\Widget\FieldPart\Hint;
21
use Yiisoft\Form\Widget\FieldPart\Label;
22
use Yiisoft\Html\Html;
23
use Yiisoft\Html\Tag\Div;
24
25
use function strtr;
26
27
/**
28
 * Renders the field widget along with label and hint tag (if any) according to template.
29
 *
30
 * @psalm-suppress MissingConstructor
31
 */
32
final class Field extends FieldAttributes
33
{
34
    private ButtonAttributes $button;
35
    private array $parts = [];
36
    private WidgetAttributes $inputWidget;
37
    private GlobalAttributes $widget;
38
39
    /**
40
     * Renders a button group widget.
41
     *
42
     * @param array $buttons List of buttons. Each array element represents a single button which can be specified as a
43
     * string or an array of the following structure:
44
     * - label: string, required, the button label.
45
     * - attributes: array, optional, the HTML attributes of the button.
46
     * - type: string, optional, the button type.
47
     * - visible: bool, optional, whether this button is visible. Defaults to true.
48
     * @param array $config the configuration array for widget factory.
49
     * @param array $attributes the HTML attributes for the widget.
50
     *
51
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
52
     *
53
     * @return static the field object itself.
54
     *
55
     * @psalm-param array<string, array|string> $buttons
56
     */
57 10
    public function buttonGroup(array $buttons, array $config = [], array $attributes = []): self
58
    {
59 10
        $new = clone $this;
60 10
        $new = $new->type('buttonGroup');
61 10
        $config = array_merge($new->getDefinitions(), $config);
62 10
        $new->button = ButtonGroup::widget($config)->attributes($attributes)->buttons($buttons);
63 10
        return $new;
64
    }
65
66
    /**
67
     * Renders a checkbox.
68
     *
69
     * This method will generate the `checked` tag attribute according to the model attribute value.
70
     *
71
     * @param FormModelInterface $formModel The model object.
72
     * @param string $attribute The attribute name or expression.
73
     * @param array $config The configuration array for widget factory.
74
     * Available methods:
75
     * [
76
     *     'enclosedByLabel()' => [false],
77
     *     'label()' => ['test-text-label'],
78
     *     'labelAttributes()' => [['class' => 'test-class']],
79
     *     'uncheckValue()' => ['0'],
80
     * ]
81
     *
82
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
83
     *
84
     * @return static the field widget instance.
85
     */
86 21
    public function checkbox(FormModelInterface $formModel, string $attribute, array $config = []): self
87
    {
88 21
        $new = clone $this;
89 21
        $new = $new->type('checkbox');
90 21
        $config = array_merge($new->getDefinitions(), $config);
91
92
        /** @var array */
93 21
        $enclosedByLabel = $config['enclosedByLabel()'] ?? [true];
94
95 21
        if ($enclosedByLabel === [true]) {
96 20
            $new->parts['{label}'] = '';
97
        }
98
99 21
        $new->inputWidget = Checkbox::widget($config)->for($formModel, $attribute);
0 ignored issues
show
Bug introduced by
The method for() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Form\Widget\FieldPart\Hint or Yiisoft\Form\Widget\FieldPart\Label or Yiisoft\Form\Widget\FieldPart\Error or Yiisoft\Form\Widget\Attribute\WidgetAttributes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

99
        $new->inputWidget = Checkbox::widget($config)->/** @scrutinizer ignore-call */ for($formModel, $attribute);
Loading history...
100 21
        return $new;
101
    }
102
103
    /**
104
     * Renders a list of checkboxes.
105
     *
106
     * A checkbox list allows multiple selection, As a result, the corresponding submitted value is an array.
107
     *
108
     * The selection of the checkbox list is taken from the value of the model attribute.
109
     *
110
     * @param FormModelInterface $formModel The model object.
111
     * @param string $attribute The attribute name or expression.
112
     * @param array $config the configuration array for widget factory.
113
     * Available methods:
114
     * [
115
     *     'containerAttributes()' => [['class' => 'test-class']],
116
     *     'containerTag()' => ['span'],
117
     *     'individualItemsAttributes()' => [[1 => ['disabled' => true], 2 => ['class' => 'test-class']],
118
     *     'items()' => [[1 => 'Female', 2 => 'Male']],
119
     *     'itemsAttributes()' => [['disabled' => true],
120
     *     'itemsFormatter()' => [
121
     *         static function (CheckboxItem $item) {
122
     *             return $item->checked
123
     *                 ? "<label><input type='checkbox' name='$item->name' value='$item->value' checked> $item->label</label>"
124
     *                 : "<label><input type='checkbox' name='$item->name' value='$item->value'> $item->label</label>";
125
     *         },
126
     *     ],
127
     *     'itemsFromValues()' => [[1 => 'Female', 2 => 'Male']],
128
     *     'separator()' => ['&#9866;'],
129
     * ]
130
     *
131
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
132
     *
133
     * @return static the field widget instance.
134
     */
135 19
    public function checkboxList(FormModelInterface $formModel, string $attribute, array $config = []): self
136
    {
137 19
        $new = clone $this;
138 19
        $new = $new->type('checkboxList');
139 19
        $config = array_merge($new->getDefinitions(), $config);
140 19
        $new->inputWidget = CheckboxList::widget($config)->for($formModel, $attribute);
141 19
        return $new;
142
    }
143
144
    /**
145
     * Renders a date widget.
146
     *
147
     * @param FormModelInterface $formModel The model object.
148
     * @param string $attribute The attribute name or expression.
149
     * @param array $config the configuration array for widget factory.
150
     *
151
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
152
     *
153
     * @return static the field widget instance.
154
     */
155 15
    public function date(FormModelInterface $formModel, string $attribute, array $config = []): self
156
    {
157 15
        $new = clone $this;
158 15
        $new = $new->type('date');
159 15
        $config = array_merge($new->getDefinitions(), $config);
160 15
        $new->inputWidget = Date::widget($config)->for($formModel, $attribute);
161 15
        return $new;
162
    }
163
164
    /**
165
     * Renders a date time widget.
166
     *
167
     * @param FormModelInterface $formModel The model object.
168
     * @param string $attribute The attribute name or expression.
169
     * @param array $config the configuration array for widget factory.
170
     *
171
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
172
     *
173
     * @return static the field widget instance.
174
     */
175 16
    public function dateTime(FormModelInterface $formModel, string $attribute, array $config = []): self
176
    {
177 16
        $new = clone $this;
178 16
        $new = $new->type('dateTime');
179 16
        $config = array_merge($new->getDefinitions(), $config);
180 16
        $new->inputWidget = DateTime::widget($config)->for($formModel, $attribute);
181 16
        return $new;
182
    }
183
184
    /**
185
     * Renders a date time local widget.
186
     *
187
     * @param FormModelInterface $formModel The model object.
188
     * @param string $attribute The attribute name or expression.
189
     * @param array $config the configuration array for widget factory.
190
     *
191
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
192
     *
193
     * @return static the field widget instance.
194
     */
195 16
    public function dateTimeLocal(FormModelInterface $formModel, string $attribute, array $config = []): self
196
    {
197 16
        $new = clone $this;
198 16
        $new = $new->type('dateTimeLocal');
199 16
        $config = array_merge($new->getDefinitions(), $config);
200 16
        $new->inputWidget = DateTimeLocal::widget($config)->for($formModel, $attribute);
201 16
        return $new;
202
    }
203
204
    /**
205
     * Renders a email widget.
206
     *
207
     * @param FormModelInterface $formModel The model object.
208
     * @param string $attribute The attribute name or expression.
209
     * @param array $config the configuration array for widget factory.
210
     *
211
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
212
     *
213
     * @return static the field widget instance.
214
     */
215 23
    public function email(FormModelInterface $formModel, string $attribute, array $config = []): self
216
    {
217 23
        $new = clone $this;
218 23
        $new = $new->type('email');
219 23
        $config = array_merge($new->getDefinitions(), $config);
220 23
        $new->inputWidget = Email::widget($config)->for($formModel, $attribute);
221 23
        return $new;
222
    }
223
224
    /**
225
     * Renders a file widget.
226
     *
227
     * @param FormModelInterface $formModel The model object.
228
     * @param string $attribute The attribute name or expression.
229
     * @param array $config the configuration array for widget factory.
230
     * Available methods:
231
     * [
232
     *     'hiddenAttributes()' => [['id' => 'test-id']],
233
     *     'uncheckValue()' => ['0'],
234
     *
235
     * ]
236
     *
237
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
238
     *
239
     * @return static the field widget instance.
240
     */
241 14
    public function file(FormModelInterface $formModel, string $attribute, array $config = []): self
242
    {
243 14
        $new = clone $this;
244 14
        $new = $new->type('file');
245 14
        $config = array_merge($new->getDefinitions(), $config);
246 14
        $new->inputWidget = File::widget($config)->for($formModel, $attribute);
247 14
        return $new;
248
    }
249
250
    /**
251
     * Renders a hidden widget.
252
     *
253
     * @param FormModelInterface $formModel The model object.
254
     * @param string $attribute The attribute name or expression.
255
     * @param array $config the configuration array for widget factory.
256
     *
257
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
258
     *
259
     * @return static the field widget instance.
260
     */
261 2
    public function hidden(FormModelInterface $formModel, string $attribute, array $config = []): self
262
    {
263 2
        $new = clone $this;
264 2
        $new = $new->type('hidden');
265 2
        $new->parts['{label}'] = '';
266 2
        $new->parts['{hint}'] = '';
267 2
        $new->parts['{error}'] = '';
268 2
        $config = array_merge($new->getDefinitions(), $config);
269 2
        $new->inputWidget = Hidden::widget($config)->for($formModel, $attribute);
270 2
        return $new;
271
    }
272
273
    /**
274
     * Renders an image widget.
275
     *
276
     * @param array $config the configuration array for widget factory.
277
     * @param array $attributes the HTML attributes for the widget.
278
     * Most used attributes:
279
     * [
280
     *     'alt' => 'test-alt',
281
     *     'height' => '100%',
282
     *     'src' => 'test-src',
283
     *     'width' => '100%',
284
     * ]
285
     *
286
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
287
     *
288
     * @return static the field object itself.
289
     */
290 12
    public function image(array $config = [], array $attributes = []): self
291
    {
292 12
        $new = clone $this;
293 12
        $new = $new->type('image');
294 12
        $new->parts['{label}'] = '';
295 12
        $new->parts['{hint}'] = '';
296 12
        $new->parts['{error}'] = '';
297 12
        $config = array_merge($new->getDefinitions(), $config);
298 12
        $new->widget = Image::widget($config)->attributes($attributes);
299 12
        return $new;
300
    }
301
302
    /**
303
     * Renders a number widget.
304
     *
305
     * @param FormModelInterface $formModel The model object.
306
     * @param string $attribute The attribute name or expression.
307
     * @param array $config the configuration array for widget factory.
308
     *
309
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
310
     *
311
     * @return static the field object itself.
312
     */
313 18
    public function number(FormModelInterface $formModel, string $attribute, array $config = []): self
314
    {
315 18
        $new = clone $this;
316 18
        $new = $new->type('number');
317 18
        $config = array_merge($new->getDefinitions(), $config);
318 18
        $new->inputWidget = Number::widget($config)->for($formModel, $attribute);
319 18
        return $new;
320
    }
321
322
    /**
323
     * Renders a password widget.
324
     *
325
     * @param FormModelInterface $formModel The model object.
326
     * @param string $attribute The attribute name or expression.
327
     * @param array $config the configuration array for widget factory.
328
     *
329
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
330
     *
331
     * @return static the field object itself.
332
     */
333 21
    public function password(FormModelInterface $formModel, string $attribute, array $config = []): self
334
    {
335 21
        $new = clone $this;
336 21
        $new = $new->type('password');
337 21
        $config = array_merge($new->getDefinitions(), $config);
338 21
        $new->inputWidget = Password::widget($config)->for($formModel, $attribute);
339 21
        return $new;
340
    }
341
342
    /**
343
     * Renders a radio widget.
344
     *
345
     * @param FormModelInterface $formModel The model object.
346
     * @param string $attribute The attribute name or expression.
347
     * @param array $config the configuration array for widget factory.
348
     * Available methods:
349
     * [
350
     *     'enclosedByLabel()' => [false],
351
     *     'label()' => ['Email:'],
352
     *     'labelAttributes()' => [['class' => 'test-class']]
353
     *     'uncheckValue()' => ['0'],
354
     * ]
355
     *
356
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
357
     *
358
     * @return static the field object itself.
359
     */
360 21
    public function radio(FormModelInterface $formModel, string $attribute, array $config = []): self
361
    {
362 21
        $new = clone $this;
363 21
        $new = $new->type('radio');
364 21
        $config = array_merge($new->getDefinitions(), $config);
365
366
        /** @var array */
367 21
        $enclosedByLabel = $config['enclosedByLabel()'] ?? [true];
368
369 21
        if ($enclosedByLabel === [true]) {
370 20
            $new->parts['{label}'] = '';
371
        }
372
373 21
        $new->inputWidget = Radio::widget($config)->for($formModel, $attribute);
374 21
        return $new;
375
    }
376
377
    /**
378
     * Renders a radio list widget.
379
     *
380
     * @param FormModelInterface $formModel The model object.
381
     * @param string $attribute The attribute name or expression.
382
     * @param array $config the configuration array for widget factory.
383
     * Available methods:
384
     * [
385
     *     'containerAttributes()' => [['class' => 'test-class']],
386
     *     'containerTag()' => ['span'],
387
     *     'items()' => [[1 => 'Female', 2 => 'Male']],
388
     *     'itemsAttributes()' => [['class' => 'test-class']],
389
     *     'individualItemsAttributes()' => [[1 => ['disabled' => true], 2 => ['class' => 'test-class']]],
390
     *     'itemsFormatter()' => [
391
     *         static function (RadioItem $item) {
392
     *             return $item->checked
393
     *                 ? "<label><input type='checkbox' name='$item->name' value='$item->value' checked> $item->label</label>"
394
     *                 : "<label><input type='checkbox' name='$item->name' value='$item->value'> $item->label</label>";
395
     *         },
396
     *     ],
397
     *     'itemsFromValues()' => [[1 => 'Female', 2 => 'Male']],
398
     *     'separator()' => [PHP_EOL],
399
     *     'uncheckValue()' => ['0'],
400
     * ]
401
     *
402
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
403
     *
404
     * @return static the field object itself.
405
     */
406 20
    public function radioList(FormModelInterface $formModel, string $attribute, array $config = []): self
407
    {
408 20
        $new = clone $this;
409 20
        $new = $new->type('radioList');
410 20
        $config = array_merge($new->getDefinitions(), $config);
411 20
        $new->inputWidget = RadioList::widget($config)->for($formModel, $attribute);
412 20
        return $new;
413
    }
414
415
    /**
416
     * Renders a range widget.
417
     *
418
     * @param FormModelInterface $formModel The model object.
419
     * @param string $attribute The attribute name or expression.
420
     * @param array $config the configuration array for widget factory.
421
     * Available methods:
422
     * [
423
     *     'outputTag()' => ['p'],
424
     *     'outputAttributes()' => [['class' => 'test-class']],
425
     * ]
426
     *
427
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
428
     *
429
     * @return static the field object itself.
430
     */
431 19
    public function range(FormModelInterface $formModel, string $attribute, array $config = []): self
432
    {
433 19
        $new = clone $this;
434 19
        $new = $new->type('range');
435 19
        $config = array_merge($new->getDefinitions(), $config);
436 19
        $new->inputWidget = Range::widget($config)->for($formModel, $attribute);
437 19
        return $new;
438
    }
439
440
    /**
441
     * Renders a reset button widget.
442
     *
443
     * @param array $config the configuration array for widget factory.
444
     * @param array $attributes the HTML attributes for the widget.
445
     *
446
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
447
     *
448
     * @return static the field object itself.
449
     */
450 9
    public function resetButton(array $config = [], array $attributes = []): self
451
    {
452 9
        $new = clone $this;
453 9
        $new = $new->type('reset');
454 9
        $config = array_merge($new->getDefinitions(), $config);
455 9
        $new->button = ResetButton::widget($config)->attributes($attributes);
456 9
        return $new;
457
    }
458
459
    /**
460
     * Renders a select widget.
461
     *
462
     * @param FormModelInterface $formModel The model object.
463
     * @param string $attribute The attribute name or expression.
464
     * @param array $config The configuration array for widget factory.
465
     * Available methods:
466
     * [
467
     *     'encode()' => [true],
468
     *     'groups()' => [['1' => ['2' => 'Moscu', '3' => 'San Petersburg']]],
469
     *     'items()' => [['1' => 'Moscu', '2' => 'San Petersburg']],
470
     *     'itemsAttributes()' => [['2' => ['disabled' => true]],
471
     *     'optionsData()' => [['1' => '<b>Moscu</b>', '2' => 'San Petersburg']],
472
     *     'prompt()' => [['text' => 'Select City Birth', 'attributes' => ['value' => '0', 'selected' => 'selected']]],
473
     *     'unselectValue()' => ['0'],
474
     * ]
475
     *
476
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
477
     *
478
     * @return static the field object itself.
479
     */
480 21
    public function select(FormModelInterface $formModel, string $attribute, array $config = []): self
481
    {
482 21
        $new = clone $this;
483 21
        $new = $new->type('select');
484 21
        $config = array_merge($new->getDefinitions(), $config);
485 21
        $new->inputWidget = Select::widget($config)->for($formModel, $attribute);
486 21
        return $new;
487
    }
488
489
    /**
490
     * Renders a submit button widget.
491
     *
492
     * @param array $config the configuration array for widget factory.
493
     * @param array $attributes the HTML attributes for the widget.
494
     *
495
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
496
     *
497
     * @return static the field object itself.
498
     */
499 19
    public function submitButton(array $config = [], array $attributes = []): self
500
    {
501 19
        $new = clone $this;
502 19
        $new = $new->type('submit');
503 19
        $config = array_merge($new->getDefinitions(), $config);
504 19
        $new->button = SubmitButton::widget($config)->attributes($attributes);
505 19
        return $new;
506
    }
507
508
    /**
509
     * Renders a text widget.
510
     *
511
     * @param FormModelInterface $formModel The model object.
512
     * @param string $attribute The attribute name or expression.
513
     * @param array $config the configuration array for widget factory.
514
     *
515
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
516
     *
517
     * @return static the field widget instance.
518
     */
519 21
    public function telephone(FormModelInterface $formModel, string $attribute, array $config = []): self
520
    {
521 21
        $new = clone $this;
522 21
        $new = $new->type('telephone');
523 21
        $config = array_merge($new->getDefinitions(), $config);
524 21
        $new->inputWidget = Telephone::widget($config)->for($formModel, $attribute);
525 21
        return $new;
526
    }
527
528
    /**
529
     * Renders a text widget.
530
     *
531
     * @param FormModelInterface $formModel The model object.
532
     * @param string $attribute The attribute name or expression.
533
     * @param array $config the configuration array for widget factory.
534
     *
535
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
536
     *
537
     * @return static the field widget instance.
538
     */
539 61
    public function text(FormModelInterface $formModel, string $attribute, array $config = []): self
540
    {
541 61
        $new = clone $this;
542 61
        $new = $new->type('text');
543 61
        $config = array_merge($new->getDefinitions(), $config);
544 61
        $new->inputWidget = Text::widget($config)->for($formModel, $attribute);
545 60
        return $new;
546
    }
547
548
    /**
549
     * Renders a text area widget.
550
     *
551
     * @param FormModelInterface $formModel The model object.
552
     * @param string $attribute The attribute name or expression.
553
     * @param array $config the configuration array for widget factory.
554
     *
555
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
556
     *
557
     * @return static the field widget instance.
558
     */
559 25
    public function textArea(FormModelInterface $formModel, string $attribute, array $config = []): self
560
    {
561 25
        $new = clone $this;
562 25
        $new = $new->type('textArea');
563 25
        $config = array_merge($new->getDefinitions(), $config);
564 25
        $new->inputWidget = TextArea::widget($config)->for($formModel, $attribute);
565 23
        return $new;
566
    }
567
568
    /**
569
     * Renders a url widget.
570
     *
571
     * @param FormModelInterface $formModel The model object.
572
     * @param string $attribute The attribute name or expression.
573
     * @param array $config the configuration array for widget factory.
574
     *
575
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
576
     *
577
     * @return static the field widget instance.
578
     */
579 23
    public function url(FormModelInterface $formModel, string $attribute, array $config = []): self
580
    {
581 23
        $new = clone $this;
582 23
        $new = $new->type('url');
583 23
        $config = array_merge($new->getDefinitions(), $config);
584 23
        $new->inputWidget = Url::widget($config)->for($formModel, $attribute);
585 23
        return $new;
586
    }
587
588
    /**
589
     * Renders the whole field.
590
     *
591
     * This method will generate the label, input tag and hint tag (if any), and assemble them into HTML according to
592
     * {@see template}.
593
     *
594
     * If (not set), the default methods will be called to generate the label and input tag, and use them as the
595
     * content.
596
     *
597
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
598
     *
599
     * @return string the rendering result.
600
     */
601 422
    protected function run(): string
602
    {
603 422
        $content = '';
604
605 422
        $div = Div::tag();
606
607 422
        if (!empty($this->inputWidget)) {
608 372
            $content .= $this->renderInputWidget();
609
        }
610
611 404
        if (!empty($this->widget)) {
612 12
            $content .= $this->widget->attributes($this->getAttributes())->render();
613
        }
614
615 404
        if (!empty($this->button)) {
616 38
            $content .= $this->button->attributes($this->getAttributes())->render();
617
        }
618
619 404
        if ($this->getContainerClass() !== '') {
620 11
            $div = $div->class($this->getContainerClass());
621
        }
622
623 404
        if ($this->getContainerAttributes() !== []) {
624 8
            $div = $div->attributes($this->getContainerAttributes());
625
        }
626
627 404
        return $this->getContainer() ? $div->content(PHP_EOL . $content . PHP_EOL)->encode(false)->render() : $content;
628
    }
629
630 372
    private function buildField(): self
631
    {
632 372
        $new = clone $this;
633
634
        // Set ariadescribedby.
635 372
        if ($new->getAriaDescribedBy() === true && $new->inputWidget instanceof InputAttributes) {
636 3
            $new->inputWidget = $new->inputWidget->ariaDescribedBy($this->inputWidget->getInputId() . '-help');
0 ignored issues
show
Bug introduced by
The method ariaDescribedBy() does not exist on Yiisoft\Form\Widget\Attribute\WidgetAttributes. It seems like you code against a sub-type of Yiisoft\Form\Widget\Attribute\WidgetAttributes such as Yiisoft\Form\Widget\Attribute\InputAttributes or Yiisoft\Form\Widget\Attribute\FieldAttributes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

636
            /** @scrutinizer ignore-call */ 
637
            $new->inputWidget = $new->inputWidget->ariaDescribedBy($this->inputWidget->getInputId() . '-help');
Loading history...
637
        }
638
639
        // Set encode.
640 372
        $new->inputWidget = $new->inputWidget->encode($new->getEncode());
641
642
        // Set input class.
643 372
        $inputClass = $new->getInputClass();
644
645 372
        if ($inputClass !== '') {
646 5
            $new->inputWidget = $new->inputWidget->class($inputClass);
647
        }
648
649
        // Set placeholder.
650 372
        $placeholder = $new->getPlaceholder() ?? $new->inputWidget->getAttributePlaceHolder();
651
652 372
        if ($new->inputWidget instanceof PlaceholderInterface && $placeholder !== '') {
653 9
            $new->inputWidget = $new->inputWidget->attributes(['placeholder' => $placeholder]);
654
        }
655
656
        // Set valid class and invalid class.
657 372
        $invalidClass = $new->getInvalidClass();
658 372
        $validClass = $new->getValidClass();
659
660 372
        if ($invalidClass !== '' && $new->inputWidget->hasError()) {
0 ignored issues
show
Bug introduced by
The method hasError() does not exist on Yiisoft\Form\Widget\Attribute\GlobalAttributes. It seems like you code against a sub-type of Yiisoft\Form\Widget\Attribute\GlobalAttributes such as Yiisoft\Form\Widget\Attribute\WidgetAttributes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

660
        if ($invalidClass !== '' && $new->inputWidget->/** @scrutinizer ignore-call */ hasError()) {
Loading history...
661 5
            $new->inputWidget = $new->inputWidget->class($invalidClass);
662 368
        } elseif ($validClass !== '' && $new->inputWidget->isValidated()) {
0 ignored issues
show
Bug introduced by
The method isValidated() does not exist on Yiisoft\Form\Widget\Attribute\GlobalAttributes. It seems like you code against a sub-type of Yiisoft\Form\Widget\Attribute\GlobalAttributes such as Yiisoft\Form\Widget\Attribute\WidgetAttributes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

662
        } elseif ($validClass !== '' && $new->inputWidget->/** @scrutinizer ignore-call */ isValidated()) {
Loading history...
663 5
            $new->inputWidget = $new->inputWidget->class($validClass);
664
        }
665
666
        // Set attributes.
667 372
        $new->inputWidget = $new->inputWidget->attributes($this->getAttributes());
668
669 372
        return $new;
670
    }
671
672
    /**
673
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
674
     */
675 352
    private function renderError(): string
676
    {
677 352
        $errorAttributes = $this->getErrorAttributes();
678 352
        $errorClass = $this->getErrorClass();
679
680 352
        if ($errorClass !== '') {
681 8
            Html::addCssClass($errorAttributes, $errorClass);
682
        }
683
684 352
        return Error::widget()
685 352
            ->attributes($errorAttributes)
686 352
            ->encode($this->getEncode())
687 352
            ->for($this->inputWidget->getFormModel(), $this->inputWidget->getAttribute())
688 352
            ->message($this->getError() ?? '')
689 352
            ->messageCallback($this->getErrorMessageCallback())
690 352
            ->tag($this->getErrorTag())
691 352
            ->render();
692
    }
693
694
    /**
695
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
696
     */
697 372
    private function renderInputWidget(): string
698
    {
699 372
        $new = clone $this;
700
701 372
        $new = $new->buildField();
702
703 372
        if (!array_key_exists('{input}', $new->parts)) {
704 372
            $new->parts['{input}'] = $new->inputWidget->render();
705
        }
706
707 354
        if (!array_key_exists('{error}', $new->parts)) {
708 353
            $new->parts['{error}'] = $this->getError() !== null ? $new->renderError() : '';
709
        }
710
711 354
        if (!array_key_exists('{hint}', $new->parts)) {
712 353
            $new->parts['{hint}'] = $new->renderHint();
713
        }
714
715 354
        if (!array_key_exists('{label}', $new->parts)) {
716 321
            $new->parts['{label}'] = $new->renderLabel();
717
        }
718
719 354
        if ($new->getDefaultTokens() !== []) {
720 2
            $new->parts = array_merge($new->getDefaultTokens(), $new->parts);
721
        }
722
723 354
        return preg_replace('/^\h*\v+/m', '', trim(strtr($new->getTemplate(), $new->parts)));
724
    }
725
726
    /**
727
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
728
     */
729 353
    private function renderHint(): string
730
    {
731 353
        $hintAttributes = $this->getHintAttributes();
732 353
        $hintClass = $this->getHintClass();
733
734 353
        if ($hintClass !== '') {
735 7
            Html::addCssClass($hintAttributes, $hintClass);
736
        }
737
738 353
        if ($this->getAriaDescribedBy() === true) {
739 3
            $hintAttributes['id'] = $this->inputWidget->getInputId() . '-help';
740
        }
741
742 353
        return Hint::widget()
743 353
            ->attributes($hintAttributes)
744 353
            ->encode($this->getEncode())
745 353
            ->for($this->inputWidget->getFormModel(), $this->inputWidget->getAttribute())
746 353
            ->hint($this->getHint())
747 353
            ->tag($this->getHintTag())
748 353
            ->render();
749
    }
750
751
    /**
752
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
753
     */
754 321
    private function renderLabel(): string
755
    {
756 321
        $labelAttributes = $this->getLabelAttributes();
757 321
        $labelClass = $this->getLabelClass();
758
759 321
        if (!array_key_exists('for', $labelAttributes)) {
760
            /** @var string */
761 320
            $labelAttributes['for'] = ArrayHelper::getValue(
762 320
                $this->getAttributes(),
763 320
                'id',
764 320
                $this->inputWidget->getInputId(),
765
            );
766
        }
767
768 321
        if ($labelClass !== '') {
769 4
            Html::addCssClass($labelAttributes, $labelClass);
770
        }
771
772 321
        return Label::widget()
773 321
            ->attributes($labelAttributes)
774 321
            ->encode($this->getEncode())
775 321
            ->for($this->inputWidget->getFormModel(), $this->inputWidget->getAttribute())
776 321
            ->label($this->getLabel())
777 321
            ->render();
778
    }
779
}
780