Passed
Pull Request — master (#192)
by Sergei
03:07
created

Field::buildField()   B

Complexity

Conditions 8
Paths 12

Size

Total Lines 33
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 9

Importance

Changes 0
Metric Value
cc 8
eloc 15
c 0
b 0
f 0
nc 12
nop 0
dl 0
loc 33
ccs 12
cts 16
cp 0.75
crap 9
rs 8.4444
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\FieldAttributes;
14
use Yiisoft\Form\Widget\Attribute\InputAttributes;
15
use Yiisoft\Form\Widget\Attribute\GlobalAttributes;
16
use Yiisoft\Form\Widget\Attribute\WidgetAttributes;
17
use Yiisoft\Form\Widget\FieldPart\Error;
18
use Yiisoft\Form\Widget\FieldPart\Hint;
19
use Yiisoft\Form\Widget\FieldPart\Label;
20
use Yiisoft\Html\Html;
21
use Yiisoft\Html\Tag\Div;
22
23
use function array_key_exists;
24
use function array_merge;
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 array $parts = [];
35
    private WidgetAttributes $inputWidget;
36
37
    /**
38
     * Renders a file widget.
39
     *
40
     * @param FormModelInterface $formModel The model object.
41
     * @param string $attribute The attribute name or expression.
42
     * @param array $config the configuration array for widget factory.
43
     * Available methods:
44
     * [
45
     *     'hiddenAttributes()' => [['id' => 'test-id']],
46
     *     'uncheckValue()' => ['0'],
47
     *
48
     * ]
49
     *
50
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
51
     *
52
     * @return self the field widget instance.
53
     */
54 13
    public function file(FormModelInterface $formModel, string $attribute, array $config = []): self
55
    {
56 13
        $new = clone $this;
57 13
        $new = $new->type('file');
58 13
        $config = array_merge($new->getDefinitions(), $config);
59 13
        $new->inputWidget = File::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

59
        $new->inputWidget = File::widget($config)->/** @scrutinizer ignore-call */ for($formModel, $attribute);
Loading history...
60 13
        return $new;
61
    }
62
63
    /**
64
     * Renders the whole field.
65
     *
66
     * This method will generate the label, input tag and hint tag (if any), and assemble them into HTML according to
67
     * {@see template}.
68
     *
69
     * If (not set), the default methods will be called to generate the label and input tag, and use them as the
70
     * content.
71
     *
72
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
73
     *
74
     * @return string the rendering result.
75
     */
76 13
    protected function run(): string
77
    {
78 13
        $content = '';
79
80 13
        $div = Div::tag();
81
82 13
        if (!empty($this->inputWidget)) {
83 13
            $content .= $this->renderInputWidget();
84
        }
85
86 13
        if ($this->getContainerClass() !== '') {
87
            $div = $div->class($this->getContainerClass());
88
        }
89
90 13
        if ($this->getContainerAttributes() !== []) {
91
            $div = $div->attributes($this->getContainerAttributes());
92
        }
93
94 13
        return $this->getContainer() ? $div->content(PHP_EOL . $content . PHP_EOL)->encode(false)->render() : $content;
95
    }
96
97 13
    private function buildField(): self
98
    {
99 13
        $new = clone $this;
100
101
        // Set ariadescribedby.
102 13
        if ($new->getAriaDescribedBy() === true && $new->inputWidget instanceof InputAttributes) {
103
            $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 said class. However, the method does not exist in anonymous//tests/Widget/...dgetAttributeTest.php$0. Are you sure you never get one of those? ( Ignorable by Annotation )

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

103
            /** @scrutinizer ignore-call */ 
104
            $new->inputWidget = $new->inputWidget->ariaDescribedBy($this->inputWidget->getInputId() . '-help');
Loading history...
104
        }
105
106
        // Set encode.
107 13
        $new->inputWidget = $new->inputWidget->encode($new->getEncode());
108
109
        // Set input class.
110 13
        $inputClass = $new->getInputClass();
111
112 13
        if ($inputClass !== '') {
113
            $new->inputWidget = $new->inputWidget->class($inputClass);
114
        }
115
116
        // Set valid class and invalid class.
117 13
        $invalidClass = $new->getInvalidClass();
118 13
        $validClass = $new->getValidClass();
119
120 13
        if ($invalidClass !== '' && $new->inputWidget->hasError()) {
121
            $new->inputWidget = $new->inputWidget->class($invalidClass);
122 13
        } elseif ($validClass !== '' && $new->inputWidget->isValidated()) {
123
            $new->inputWidget = $new->inputWidget->class($validClass);
124
        }
125
126
        // Set attributes.
127 13
        $new->inputWidget = $new->inputWidget->attributes($this->getAttributes());
128
129 13
        return $new;
130
    }
131
132
    /**
133
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
134
     */
135 13
    private function renderError(): string
136
    {
137 13
        $errorAttributes = $this->getErrorAttributes();
138 13
        $errorClass = $this->getErrorClass();
139
140 13
        if ($errorClass !== '') {
141
            Html::addCssClass($errorAttributes, $errorClass);
142
        }
143
144 13
        return Error::widget()
145 13
            ->attributes($errorAttributes)
0 ignored issues
show
Bug introduced by
The method attributes() 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\GlobalAttributes or Yiisoft\Form\Field\Base\ButtonField. ( Ignorable by Annotation )

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

145
            ->/** @scrutinizer ignore-call */ attributes($errorAttributes)
Loading history...
146 13
            ->encode($this->getEncode())
147 13
            ->for($this->inputWidget->getFormModel(), $this->inputWidget->getAttribute())
148 13
            ->message($this->getError() ?? '')
149 13
            ->messageCallback($this->getErrorMessageCallback())
150 13
            ->tag($this->getErrorTag())
151 13
            ->render();
152
    }
153
154
    /**
155
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
156
     */
157 13
    private function renderInputWidget(): string
158
    {
159 13
        $new = clone $this;
160
161 13
        $new = $new->buildField();
162
163 13
        if (!array_key_exists('{input}', $new->parts)) {
164 13
            $new->parts['{input}'] = $new->inputWidget->render();
165
        }
166
167 13
        if (!array_key_exists('{error}', $new->parts)) {
168 13
            $new->parts['{error}'] = $this->getError() !== null ? $new->renderError() : '';
169
        }
170
171 13
        if (!array_key_exists('{hint}', $new->parts)) {
172 13
            $new->parts['{hint}'] = $new->renderHint();
173
        }
174
175 13
        if (!array_key_exists('{label}', $new->parts)) {
176 13
            $new->parts['{label}'] = $new->renderLabel();
177
        }
178
179 13
        if ($new->getDefaultTokens() !== []) {
180
            $new->parts = array_merge($new->parts, $new->getDefaultTokens());
181
        }
182
183 13
        return preg_replace('/^\h*\v+/m', '', trim(strtr($new->getTemplate(), $new->parts)));
184
    }
185
186
    /**
187
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
188
     */
189 13
    private function renderHint(): string
190
    {
191 13
        $hintAttributes = $this->getHintAttributes();
192 13
        $hintClass = $this->getHintClass();
193
194 13
        if ($hintClass !== '') {
195
            Html::addCssClass($hintAttributes, $hintClass);
196
        }
197
198 13
        if ($this->getAriaDescribedBy() === true) {
199
            $hintAttributes['id'] = $this->inputWidget->getInputId() . '-help';
200
        }
201
202 13
        return Hint::widget()
203 13
            ->attributes($hintAttributes)
204 13
            ->encode($this->getEncode())
205 13
            ->for($this->inputWidget->getFormModel(), $this->inputWidget->getAttribute())
206 13
            ->hint($this->getHint())
207 13
            ->tag($this->getHintTag())
208 13
            ->render();
209
    }
210
211
    /**
212
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
213
     */
214 13
    private function renderLabel(): string
215
    {
216 13
        $labelAttributes = $this->getLabelAttributes();
217 13
        $labelClass = $this->getLabelClass();
218
219 13
        if (!array_key_exists('for', $labelAttributes)) {
220
            /** @var string */
221 13
            $labelAttributes['for'] = ArrayHelper::getValue(
222 13
                $this->getAttributes(),
223
                'id',
224 13
                $this->inputWidget->getInputId(),
225
            );
226
        }
227
228 13
        if ($labelClass !== '') {
229
            Html::addCssClass($labelAttributes, $labelClass);
230
        }
231
232 13
        return Label::widget()
233 13
            ->attributes($labelAttributes)
234 13
            ->encode($this->getEncode())
235 13
            ->for($this->inputWidget->getFormModel(), $this->inputWidget->getAttribute())
236 13
            ->label($this->getLabel())
237 13
            ->render();
238
    }
239
}
240