Test Failed
Pull Request — master (#192)
by Sergei
02:42
created

PartsField::generateLabel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
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 Stringable;
9
use Yiisoft\Form\Field\Part\Error;
10
use Yiisoft\Form\Field\Part\Hint;
11
use Yiisoft\Form\Field\Part\Label;
12
13
use function in_array;
14
15
abstract class PartsField extends BaseField
16
{
17
    private const BUILTIN_TOKENS = [
18
        '{input}',
19
        '{label}',
20
        '{hint}',
21
        '{error}',
22
    ];
23
24
    /**
25
     * @var string[]|Stringable[]
26
     * @psalm-var array<non-empty-string,string|Stringable>
27
     */
28
    private array $extraTokens = [];
29
30
    protected string $templateBegin = "{label}\n{input}";
31
    protected string $templateEnd = "{input}\n{hint}\n{error}";
32
    protected string $template = "{label}\n{input}\n{hint}\n{error}";
33
    protected ?bool $hideLabel = null;
34
35
    private array $labelConfig = [];
36
    private array $hintConfig = [];
37
    private array $errorConfig = [];
38
39
    final public function tokens(array $tokens): static
40
    {
41
        $new = clone $this;
42
43
        foreach ($tokens as $token => $value) {
44
            if (!is_string($token)) {
45
                throw new InvalidArgumentException(
46
                    sprintf(
47
                        'Token should be string. %s given.',
48
                        $token,
49
                    )
50
                );
51
            }
52
53
            if (!is_string($value) && !$value instanceof Stringable) {
54
                throw new InvalidArgumentException(
55
                    sprintf(
56
                        'Token value should be string or Stringable. %s given.',
57
                        get_debug_type($value),
58
                    )
59
                );
60
            }
61
62
            $this->validateToken($token);
63
64
            $new->extraTokens[$token] = $value;
65
        }
66
67
        return $new;
68
    }
69
70
    final public function token(string $token, string|Stringable $value): static
71
    {
72
        $this->validateToken($token);
73
74
        $new = clone $this;
75
        $new->extraTokens[$token] = $value;
76
        return $new;
77
    }
78
79
    /**
80
     * Set layout template for render a field.
81
     */
82
    final public function template(string $template): static
83
    {
84
        $new = clone $this;
85
        $new->template = $template;
86
        return $new;
87
    }
88
89
    final public function templateBegin(string $template): static
90
    {
91
        $new = clone $this;
92
        $new->templateBegin = $template;
93
        return $new;
94
    }
95
96
    final public function templateEnd(string $template): static
97
    {
98
        $new = clone $this;
99
        $new->templateEnd = $template;
100
        return $new;
101
    }
102
103
    final public function hideLabel(?bool $hide = true): static
104
    {
105
        $new = clone $this;
106
        $new->hideLabel = $hide;
107
        return $new;
108
    }
109
110
    final public function labelConfig(array $config): static
111
    {
112
        $new = clone $this;
113
        $new->labelConfig = $config;
114
        return $new;
115
    }
116
117
    final public function label(?string $content): static
118
    {
119
        $new = clone $this;
120
        $new->labelConfig['content()'] = [$content];
121
        return $new;
122
    }
123
124
    final public function hintConfig(array $config): static
125
    {
126
        $new = clone $this;
127
        $new->hintConfig = $config;
128
        return $new;
129
    }
130
131
    final public function hint(?string $content): static
132
    {
133
        $new = clone $this;
134
        $new->hintConfig['content()'] = [$content];
135
        return $new;
136
    }
137
138
    final public function errorConfig(array $config): static
139
    {
140
        $new = clone $this;
141
        $new->errorConfig = $config;
142
        return $new;
143
    }
144
145
    final public function error(?string $message): static
146
    {
147
        $new = clone $this;
148
        $new->errorConfig['message()'] = [$message];
149
        return $new;
150
    }
151
152
    protected function shouldHideLabel(): bool
153
    {
154
        return false;
155
    }
156
157
    protected function generateInput(): string
158
    {
159
        return '';
160
    }
161
162
    protected function generateBeginInput(): string
163
    {
164
        return '';
165
    }
166
167
    protected function generateEndInput(): string
168
    {
169
        return '';
170
    }
171
172
    protected function renderLabel(Label $label): string
173
    {
174
        return $label->render();
175
    }
176
177
    protected function renderHint(Hint $hint): string
178
    {
179
        return $hint->render();
180
    }
181
182
    protected function renderError(Error $error): string
183
    {
184
        return $error->render();
185
    }
186
187
    final protected function generateContent(): ?string
188
    {
189
        $parts = [
190
            '{input}' => $this->generateInput(),
191
            '{label}' => ($this->hideLabel ?? $this->shouldHideLabel()) ? '' : $this->generateLabel(),
192
            '{hint}' => $this->generateHint(),
193
            '{error}' => $this->generateError(),
194
        ];
195
196
        return $this->makeContent($this->template, $parts);
197
    }
198
199
    final protected function generateBeginContent(): string
200
    {
201
        $parts = [
202
            '{input}' => $this->generateBeginInput(),
203
            '{label}' => ($this->hideLabel ?? $this->shouldHideLabel()) ? '' : $this->generateLabel(),
204
            '{hint}' => $this->generateHint(),
205
            '{error}' => $this->generateError(),
206
        ];
207
208
        return $this->makeContent($this->templateBegin, $parts);
209
    }
210
211
    final protected function generateEndContent(): string
212
    {
213
        $parts = [
214
            '{input}' => $this->generateEndInput(),
215
            '{label}' => ($this->hideLabel ?? $this->shouldHideLabel()) ? '' : $this->generateLabel(),
216
            '{hint}' => $this->generateHint(),
217
            '{error}' => $this->generateError(),
218
        ];
219
220
        return $this->makeContent($this->templateEnd, $parts);
221
    }
222
223
    private function makeContent(string $template, array $parts): string
224
    {
225
        if (!empty($this->extraTokens)) {
226
            $parts += $this->extraTokens;
227
        }
228
229
        return preg_replace('/^\h*\v+/m', '', trim(strtr($template, $parts)));
230
    }
231
232
    private function generateLabel(): string
233
    {
234
        $label = Label::widget($this->labelConfig);
235
236
        return $this->renderLabel($label);
237
    }
238
239
    private function generateHint(): string
240
    {
241
        $hint = Hint::widget($this->hintConfig);
242
243
        return $this->renderHint($hint);
244
    }
245
246
    private function generateError(): string
247
    {
248
        $error = Error::widget($this->errorConfig);
249
250
        return $this->renderError($error);
251
    }
252
253
    /**
254
     * @psalm-assert non-empty-string $token
255
     */
256
    private function validateToken(string $token): void
257
    {
258
        if ($token === '') {
259
            throw new InvalidArgumentException('Token must be non-empty string.');
260
        }
261
262
        if (in_array($token, self::BUILTIN_TOKENS, true)) {
263
            throw new InvalidArgumentException(
264
                sprintf(
265
                    'Token name "%s" is built-in.',
266
                    $token,
267
                )
268
            );
269
        }
270
    }
271
}
272