Passed
Pull Request — master (#159)
by Sergei
11:02
created

AbstractField::run()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
nc 3
nop 0
dl 0
loc 16
ccs 11
cts 11
cp 1
crap 3
rs 9.9332
c 1
b 0
f 0
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
/**
18
 * @psalm-import-type HtmlAttributes from \Yiisoft\Html\Html
19
 */
20
abstract class AbstractField extends Widget
21
{
22
    use FormAttributeTrait;
23
24
    /**
25
     * @psalm-var non-empty-string
26
     */
27
    private string $containerTag = 'div';
28
    private array $containerTagAttributes = [];
29
    private bool $useContainer = true;
30
31
    private string $template = "{label}\n{input}\n{hint}\n{error}";
32
33
    private ?string $inputId = null;
34
    private ?string $inputIdFromTag = null;
35
    private bool $setInputIdAttribute = true;
36
37
    /**
38
     * @psalm-var HtmlAttributes
39
     */
40
    private array $formElementTagAttributes = [];
41
42
    private array $labelConfig = [];
43
    private array $hintConfig = [];
44
    private array $errorConfig = [];
45
46
    /**
47
     * @return static
48
     */
49 5
    final public function containerTag(string $tag): self
50
    {
51 5
        if ($tag === '') {
52 1
            throw new InvalidArgumentException('Tag name cannot be empty.');
53
        }
54
55 4
        $new = clone $this;
56 4
        $new->containerTag = $tag;
57 4
        return $new;
58
    }
59
60
    /**
61
     * @return static
62
     */
63 4
    final public function containerTagAttributes(array $attributes): self
64
    {
65 4
        $new = clone $this;
66 4
        $new->containerTagAttributes = $attributes;
67 4
        return $new;
68
    }
69
70 2
    final public function useContainer(bool $use): self
71
    {
72 2
        $new = clone $this;
73 2
        $new->useContainer = $use;
74 2
        return $new;
75
    }
76
77
    /**
78
     * Set layout template for render a field.
79
     *
80
     * @param string $template
81
     *
82
     * @return static
83
     */
84 2
    final public function template(string $template): self
85
    {
86 2
        $new = clone $this;
87 2
        $new->template = $template;
88 2
        return $new;
89
    }
90
91
    /**
92
     * @return static
93
     */
94 2
    final public function inputId(?string $inputId): self
95
    {
96 2
        $new = clone $this;
97 2
        $new->inputId = $inputId;
98 2
        return $new;
99
    }
100
101
    /**
102
     * @return static
103
     */
104 2
    final public function setInputIdAttribute(bool $value): self
105
    {
106 2
        $new = clone $this;
107 2
        $new->setInputIdAttribute = $value;
108 2
        return $new;
109
    }
110
111
    /**
112
     * @psalm-param HtmlAttributes $attributes
113
     */
114 6
    final public function formElementTagAttributes(array $attributes): self
115
    {
116 6
        $new = clone $this;
117 6
        $new->formElementTagAttributes = $attributes;
118 6
        return $new;
119
    }
120
121
    /**
122
     * @return static
123
     */
124 3
    final public function labelConfig(array $config): self
125
    {
126 3
        $new = clone $this;
127 3
        $new->labelConfig = $config;
128 3
        return $new;
129
    }
130
131
    /**
132
     * @return static
133
     */
134 1
    final public function label(?string $content): self
135
    {
136 1
        $new = clone $this;
137 1
        $new->labelConfig['content()'] = [$content];
138 1
        return $new;
139
    }
140
141
    /**
142
     * @return static
143
     */
144 2
    final public function hintConfig(array $config): self
145
    {
146 2
        $new = clone $this;
147 2
        $new->hintConfig = $config;
148 2
        return $new;
149
    }
150
151 1
    final public function hint(?string $content): self
152
    {
153 1
        $new = clone $this;
154 1
        $new->hintConfig['content()'] = [$content];
155 1
        return $new;
156
    }
157
158
    /**
159
     * @return static
160
     */
161 2
    final public function errorConfig(array $config): self
162
    {
163 2
        $new = clone $this;
164 2
        $new->errorConfig = $config;
165 2
        return $new;
166
    }
167
168 29
    final protected function getInputName(): string
169
    {
170 29
        return HtmlForm::getInputName($this->getFormModel(), $this->attribute);
171
    }
172
173
    /**
174
     * @psalm-return HtmlAttributes
175
     */
176 29
    final protected function getFormElementTagAttributes(): array
177
    {
178 29
        $attributes = $this->formElementTagAttributes;
179
180 29
        $this->prepareIdInFormElementTagAttributes($attributes);
181
182 29
        if ($this->isUsePlaceholder()) {
183
            /** @psalm-suppress UndefinedMethod */
184 29
            $this->preparePlaceholderInFormElementTagAttributes($attributes);
0 ignored issues
show
Bug introduced by
The method preparePlaceholderInFormElementTagAttributes() does not exist on Yiisoft\Form\Field\Base\AbstractField. Since it exists in all sub-types, consider adding an abstract or default implementation to Yiisoft\Form\Field\Base\AbstractField. ( Ignorable by Annotation )

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

184
            $this->/** @scrutinizer ignore-call */ 
185
                   preparePlaceholderInFormElementTagAttributes($attributes);
Loading history...
185
        }
186
187
        /** @psalm-var HtmlAttributes $attributes */
188
189 29
        return $attributes;
190
    }
191
192 29
    final protected function prepareIdInFormElementTagAttributes(array &$attributes): void
193
    {
194
        /** @var mixed $idFromTag */
195 29
        $idFromTag = $attributes['id'] ?? null;
196 29
        if ($idFromTag !== null) {
197 2
            $this->inputIdFromTag = (string) $idFromTag;
198
        }
199
200 29
        if ($this->setInputIdAttribute) {
201 27
            if ($this->inputId !== null) {
202 2
                $attributes['id'] = $this->inputId;
203 25
            } elseif ($idFromTag === null) {
204 24
                $attributes['id'] = $this->getInputId();
205
            }
206
        }
207 29
    }
208
209 29
    final protected function run(): string
210
    {
211 29
        if (!$this->useContainer) {
212 2
            return $this->generateContent();
213
        }
214
215 27
        $containerTag = CustomTag::name($this->containerTag);
216 27
        if ($this->containerTagAttributes !== []) {
217 4
            $containerTag = $containerTag->attributes($this->containerTagAttributes);
218
        }
219
220 27
        return $containerTag->open()
221 27
            . PHP_EOL
222 27
            . $this->generateContent()
223 27
            . PHP_EOL
224 27
            . $containerTag->close();
225
    }
226
227
    abstract protected function generateInput(): string;
228
229 29
    private function generateContent(): string
230
    {
231 29
        $parts = [
232 29
            '{input}' => $this->generateInput(),
233 29
            '{label}' => $this->generateLabel(),
234 29
            '{hint}' => $this->generateHint(),
235 29
            '{error}' => $this->generateError(),
236
        ];
237
238 29
        return preg_replace('/^\h*\v+/m', '', trim(strtr($this->template, $parts)));
239
    }
240
241 29
    private function generateLabel(): string
242
    {
243 29
        $label = Label::widget($this->labelConfig)
244 29
            ->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

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