Passed
Pull Request — master (#192)
by Alexander
02:31
created

AbstractField   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 102
dl 0
loc 262
ccs 110
cts 110
cp 1
rs 9.6
c 1
b 0
f 0
wmc 35

23 Methods

Rating   Name   Duplication   Size   Complexity  
A containerTagAttributes() 0 5 1
A containerTag() 0 9 2
A getInputName() 0 3 1
A useContainer() 0 5 1
A generateContent() 0 10 2
A prepareIdInInputTagAttributes() 0 13 5
A run() 0 16 3
A shouldHideLabel() 0 3 1
A hintConfig() 0 5 1
A isUsePlaceholder() 0 4 1
A hideLabel() 0 5 1
A setInputIdAttribute() 0 5 1
A inputTagAttributes() 0 5 1
A inputId() 0 5 1
A getInputTagAttributes() 0 12 2
A hint() 0 5 1
A labelConfig() 0 5 1
A generateError() 0 5 1
A generateLabel() 0 16 4
A generateHint() 0 5 1
A template() 0 5 1
A errorConfig() 0 5 1
A label() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form\Field\Base;
6
7
use InvalidArgumentException;
8
use Yiisoft\Form\Field\Part\Error;
9
use Yiisoft\Form\Field\Part\Hint;
10
use Yiisoft\Form\Field\Part\Label;
11
use Yiisoft\Form\Helper\HtmlForm;
12
use Yiisoft\Html\Tag\CustomTag;
13
use Yiisoft\Widget\Widget;
14
15
use function in_array;
16
17
abstract class AbstractField extends Widget
18
{
19
    use FormAttributeTrait;
20
21
    /**
22
     * @psalm-var non-empty-string
23
     */
24
    protected string $containerTag = 'div';
25
    protected array $containerTagAttributes = [];
26
    protected bool $useContainer = true;
27
28
    protected string $template = "{label}\n{input}\n{hint}\n{error}";
29
    protected ?bool $hideLabel = null;
30
31
    protected ?string $inputId = null;
32
    protected ?string $inputIdFromTag = null;
33
    protected bool $setInputIdAttribute = true;
34
35
    protected array $inputTagAttributes = [];
36
37
    protected array $labelConfig = [];
38
    protected array $hintConfig = [];
39
    protected array $errorConfig = [];
40
41
    /**
42
     * @return static
43
     */
44 4
    final public function containerTag(string $tag): self
45
    {
46 4
        if ($tag === '') {
47 1
            throw new InvalidArgumentException('Tag name cannot be empty.');
48
        }
49
50 3
        $new = clone $this;
51 3
        $new->containerTag = $tag;
52 3
        return $new;
53
    }
54
55
    /**
56
     * @return static
57
     */
58 3
    final public function containerTagAttributes(array $attributes): self
59
    {
60 3
        $new = clone $this;
61 3
        $new->containerTagAttributes = $attributes;
62 3
        return $new;
63
    }
64
65 2
    final public function useContainer(bool $use): self
66
    {
67 2
        $new = clone $this;
68 2
        $new->useContainer = $use;
69 2
        return $new;
70
    }
71
72
    /**
73
     * Set layout template for render a field.
74
     *
75
     * @param string $template
76
     *
77
     * @return static
78
     */
79 2
    final public function template(string $template): self
80
    {
81 2
        $new = clone $this;
82 2
        $new->template = $template;
83 2
        return $new;
84
    }
85
86 2
    final public function hideLabel(?bool $hide = true): self
87
    {
88 2
        $new = clone $this;
89 2
        $new->hideLabel = $hide;
90 2
        return $new;
91
    }
92
93
    /**
94
     * @return static
95
     */
96 2
    final public function inputId(?string $inputId): self
97
    {
98 2
        $new = clone $this;
99 2
        $new->inputId = $inputId;
100 2
        return $new;
101
    }
102
103
    /**
104
     * @return static
105
     */
106 2
    final public function setInputIdAttribute(bool $value): self
107
    {
108 2
        $new = clone $this;
109 2
        $new->setInputIdAttribute = $value;
110 2
        return $new;
111
    }
112
113 6
    final public function inputTagAttributes(array $attributes): self
114
    {
115 6
        $new = clone $this;
116 6
        $new->inputTagAttributes = $attributes;
117 6
        return $new;
118
    }
119
120
    /**
121
     * @return static
122
     */
123 3
    final public function labelConfig(array $config): self
124
    {
125 3
        $new = clone $this;
126 3
        $new->labelConfig = $config;
127 3
        return $new;
128
    }
129
130
    /**
131
     * @return static
132
     */
133 1
    final public function label(?string $content): self
134
    {
135 1
        $new = clone $this;
136 1
        $new->labelConfig['content()'] = [$content];
137 1
        return $new;
138
    }
139
140
    /**
141
     * @return static
142
     */
143 2
    final public function hintConfig(array $config): self
144
    {
145 2
        $new = clone $this;
146 2
        $new->hintConfig = $config;
147 2
        return $new;
148
    }
149
150
    /**
151
     * @return static
152
     */
153 1
    final public function hint(?string $content): self
154
    {
155 1
        $new = clone $this;
156 1
        $new->hintConfig['content()'] = [$content];
157 1
        return $new;
158
    }
159
160
    /**
161
     * @return static
162
     */
163 2
    final public function errorConfig(array $config): self
164
    {
165 2
        $new = clone $this;
166 2
        $new->errorConfig = $config;
167 2
        return $new;
168
    }
169
170 59
    final protected function getInputName(): string
171
    {
172 59
        return HtmlForm::getInputName($this->getFormModel(), $this->attribute);
173
    }
174
175 59
    final protected function getInputTagAttributes(): array
176
    {
177 59
        $attributes = $this->inputTagAttributes;
178
179 59
        $this->prepareIdInInputTagAttributes($attributes);
180
181 59
        if ($this->isUsePlaceholder()) {
182
            /** @psalm-suppress UndefinedMethod */
183 34
            $this->preparePlaceholderInInputTagAttributes($attributes);
0 ignored issues
show
Bug introduced by
The method preparePlaceholderInInputTagAttributes() does not exist on Yiisoft\Form\Field\Base\AbstractField. It seems like you code against a sub-type of Yiisoft\Form\Field\Base\AbstractField such as Yiisoft\Form\Field\Email or Yiisoft\Form\Field\Text. ( Ignorable by Annotation )

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

183
            $this->/** @scrutinizer ignore-call */ 
184
                   preparePlaceholderInInputTagAttributes($attributes);
Loading history...
184
        }
185
186 59
        return $attributes;
187
    }
188
189 59
    final protected function prepareIdInInputTagAttributes(array &$attributes): void
190
    {
191
        /** @var mixed $idFromTag */
192 59
        $idFromTag = $attributes['id'] ?? null;
193 59
        if ($idFromTag !== null) {
194 2
            $this->inputIdFromTag = (string)$idFromTag;
195
        }
196
197 59
        if ($this->setInputIdAttribute) {
198 57
            if ($this->inputId !== null) {
199 2
                $attributes['id'] = $this->inputId;
200 55
            } elseif ($idFromTag === null) {
201 54
                $attributes['id'] = $this->getInputId();
202
            }
203
        }
204
    }
205
206 60
    final protected function run(): string
207
    {
208 60
        if (!$this->useContainer) {
209 3
            return $this->generateContent();
210
        }
211
212 57
        $containerTag = CustomTag::name($this->containerTag);
213 57
        if ($this->containerTagAttributes !== []) {
214 3
            $containerTag = $containerTag->attributes($this->containerTagAttributes);
215
        }
216
217 57
        return $containerTag->open()
218
            . PHP_EOL
219 57
            . $this->generateContent()
220
            . PHP_EOL
221 56
            . $containerTag->close();
222
    }
223
224
    abstract protected function generateInput(): string;
225
226 39
    protected function shouldHideLabel(): bool
227
    {
228 39
        return false;
229
    }
230
231 60
    private function generateContent(): string
232
    {
233 59
        $parts = [
234 60
            '{input}' => $this->generateInput(),
235 59
            '{label}' => ($this->hideLabel ?? $this->shouldHideLabel()) ? '' : $this->generateLabel(),
236 59
            '{hint}' => $this->generateHint(),
237 59
            '{error}' => $this->generateError(),
238
        ];
239
240 59
        return preg_replace('/^\h*\v+/m', '', trim(strtr($this->template, $parts)));
241
    }
242
243 44
    private function generateLabel(): string
244
    {
245 44
        $label = Label::widget($this->labelConfig)
246 44
            ->attribute($this->getFormModel(), $this->attribute);
0 ignored issues
show
Bug introduced by
The method attribute() does not exist on Yiisoft\Widget\Widget. It seems like you code against a sub-type of Yiisoft\Widget\Widget such as Yiisoft\Form\Field\Part\Label or Yiisoft\Form\Field\Base\AbstractField or Yiisoft\Form\Field\Part\Error or Yiisoft\Form\Field\Part\Hint. ( Ignorable by Annotation )

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

246
            ->/** @scrutinizer ignore-call */ attribute($this->getFormModel(), $this->attribute);
Loading history...
247
248 44
        if ($this->setInputIdAttribute === false) {
249 2
            $label = $label->useInputIdAttribute(false);
250
        }
251
252 44
        if ($this->inputId !== null) {
253 2
            $label = $label->forId($this->inputId);
254 42
        } elseif ($this->inputIdFromTag !== null) {
255 1
            $label = $label->forId($this->inputIdFromTag);
256
        }
257
258 44
        return $label->render();
259
    }
260
261 59
    private function generateHint(): string
262
    {
263 59
        return Hint::widget($this->hintConfig)
264 59
            ->attribute($this->getFormModel(), $this->attribute)
265 59
            ->render();
266
    }
267
268 59
    private function generateError(): string
269
    {
270 59
        return Error::widget($this->errorConfig)
271 59
            ->attribute($this->getFormModel(), $this->attribute)
272 59
            ->render();
273
    }
274
275 59
    private function isUsePlaceholder(): bool
276
    {
277 59
        $traits = class_uses($this);
278 59
        return in_array(PlaceholderTrait::class, $traits, true);
279
    }
280
}
281