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

Field::renderError()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 2.0017

Importance

Changes 0
Metric Value
cc 2
eloc 12
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 17
ccs 12
cts 13
cp 0.9231
crap 2.0017
rs 9.8666
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\WidgetAttributes;
16
use Yiisoft\Form\Widget\FieldPart\Error;
17
use Yiisoft\Form\Widget\FieldPart\Hint;
18
use Yiisoft\Form\Widget\FieldPart\Label;
19
use Yiisoft\Html\Html;
20
use Yiisoft\Html\Tag\Div;
21
22
use function array_key_exists;
23
use function array_merge;
24
use function strtr;
25
26
/**
27
 * Renders the field widget along with label and hint tag (if any) according to template.
28
 *
29
 * @psalm-suppress MissingConstructor
30
 */
31
final class Field extends FieldAttributes
32
{
33
    private array $parts = [];
34
    private WidgetAttributes $inputWidget;
35
36
    /**
37
     * Renders a file widget.
38
     *
39
     * @param FormModelInterface $formModel The model object.
40
     * @param string $attribute The attribute name or expression.
41
     * @param array $config the configuration array for widget factory.
42
     * Available methods:
43
     * [
44
     *     'hiddenAttributes()' => [['id' => 'test-id']],
45
     *     'uncheckValue()' => ['0'],
46
     *
47
     * ]
48
     *
49
     * @throws CircularReferenceException|InvalidConfigException|NotFoundException|NotInstantiableException
50
     *
51
     * @return self the field widget instance.
52
     */
53 13
    public function file(FormModelInterface $formModel, string $attribute, array $config = []): self
54
    {
55 13
        $new = clone $this;
56 13
        $new = $new->type('file');
57 13
        $config = array_merge($new->getDefinitions(), $config);
58 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

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

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

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