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

FieldFactory   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 261
Duplicated Lines 0 %

Test Coverage

Coverage 97.09%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 85
dl 0
loc 261
ccs 100
cts 103
cp 0.9709
rs 8.8798
c 3
b 1
f 0
wmc 44

27 Methods

Rating   Name   Duplication   Size   Complexity  
A resetButton() 0 3 1
A label() 0 7 1
A range() 0 3 1
A __construct() 0 13 1
A hidden() 0 3 1
A textarea() 0 3 1
A text() 0 3 1
A input() 0 13 2
A button() 0 3 1
A error() 0 7 1
A makeLabelConfig() 0 9 2
A date() 0 3 1
A password() 0 3 1
A dateTimeLocal() 0 3 1
F makeBaseFieldConfig() 0 43 11
A url() 0 3 1
A number() 0 3 1
A image() 0 3 1
A email() 0 3 1
A hint() 0 7 1
A makeFieldConfig() 0 13 4
A field() 0 21 3
A telephone() 0 3 1
A submitButton() 0 3 1
A select() 0 3 1
A dateTime() 0 3 1
A checkbox() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like FieldFactory often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FieldFactory, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Form;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
10
use Yiisoft\Form\Field\Base\AbstractInputField;
11
use Yiisoft\Form\Field\Base\AbstractField;
12
use Yiisoft\Form\Field\Base\PlaceholderTrait;
13
use Yiisoft\Form\Field\Button;
14
use Yiisoft\Form\Field\Checkbox;
15
use Yiisoft\Form\Field\Date;
16
use Yiisoft\Form\Field\DateTime;
17
use Yiisoft\Form\Field\DateTimeLocal;
18
use Yiisoft\Form\Field\Email;
19
use Yiisoft\Form\Field\Hidden;
20
use Yiisoft\Form\Field\Image;
21
use Yiisoft\Form\Field\Number;
22
use Yiisoft\Form\Field\Part\Error;
23
use Yiisoft\Form\Field\Part\Hint;
24
use Yiisoft\Form\Field\Part\Label;
25
use Yiisoft\Form\Field\Password;
26
use Yiisoft\Form\Field\Range;
27
use Yiisoft\Form\Field\ResetButton;
28
use Yiisoft\Form\Field\Select;
29
use Yiisoft\Form\Field\SubmitButton;
30
use Yiisoft\Form\Field\Telephone;
31
use Yiisoft\Form\Field\Text;
32
use Yiisoft\Form\Field\Textarea;
33
use Yiisoft\Form\Field\Url;
34
use Yiisoft\Widget\WidgetFactory;
35
36
use function in_array;
37
38
final class FieldFactory
39
{
40
    private const SUPPORT_PLACEHOLDER = 1;
41
42
    private ?array $baseFieldConfig = null;
43
44
    /**
45
     * @param array[] $fieldConfigs
46
     */
47 17
    public function __construct(
48
        private ?string $containerTag = null,
49
        private array $containerTagAttributes = [],
50
        private ?bool $useContainer = null,
51
        private ?string $template = null,
52
        private ?bool $setInputIdAttribute = null,
53
        private array $inputTagAttributes = [],
54
        private array $labelConfig = [],
55
        private array $hintConfig = [],
56
        private array $errorConfig = [],
57
        private ?bool $usePlaceholder = null,
58
        private array $fieldConfigs = [],
59
    ) {
60
    }
61
62 1
    public function button(array $config = []): Button
63
    {
64 1
        return $this->field(Button::class, $config);
65
    }
66
67 1
    public function checkbox(FormModelInterface $formModel, string $attribute, array $config = []): Checkbox
68
    {
69 1
        return $this->input(Checkbox::class, $formModel, $attribute, $config);
70
    }
71
72 1
    public function date(FormModelInterface $formModel, string $attribute, array $config = []): Date
73
    {
74 1
        return $this->input(Date::class, $formModel, $attribute, $config);
75
    }
76
77 1
    public function dateTime(FormModelInterface $formModel, string $attribute, array $config = []): DateTime
78
    {
79 1
        return $this->input(DateTime::class, $formModel, $attribute, $config);
80
    }
81
82 1
    public function dateTimeLocal(FormModelInterface $formModel, string $attribute, array $config = []): DateTimeLocal
83
    {
84 1
        return $this->input(DateTimeLocal::class, $formModel, $attribute, $config);
85
    }
86
87 1
    public function email(FormModelInterface $formModel, string $attribute, array $config = []): Email
88
    {
89 1
        return $this->input(Email::class, $formModel, $attribute, $config);
90
    }
91
92 1
    public function hidden(FormModelInterface $formModel, string $attribute, array $config = []): Hidden
93
    {
94 1
        return $this->input(Hidden::class, $formModel, $attribute, $config);
95
    }
96
97 1
    public function image(array $config = []): Image
98
    {
99 1
        return $this->field(Image::class, $config);
100
    }
101
102 1
    public function number(FormModelInterface $formModel, string $attribute, array $config = []): Number
103
    {
104 1
        return $this->input(Number::class, $formModel, $attribute, $config);
105
    }
106
107 1
    public function password(FormModelInterface $formModel, string $attribute, array $config = []): Password
108
    {
109 1
        return $this->input(Password::class, $formModel, $attribute, $config);
110
    }
111
112 1
    public function range(FormModelInterface $formModel, string $attribute, array $config = []): Range
113
    {
114 1
        return $this->input(Range::class, $formModel, $attribute, $config);
115
    }
116
117 1
    public function resetButton(array $config = []): ResetButton
118
    {
119 1
        return $this->field(ResetButton::class, $config);
120
    }
121
122 1
    public function select(FormModelInterface $formModel, string $attribute, array $config = []): Select
123
    {
124 1
        return $this->input(Select::class, $formModel, $attribute, $config);
125
    }
126
127 1
    public function submitButton(array $config = []): SubmitButton
128
    {
129 1
        return $this->field(SubmitButton::class, $config);
130
    }
131
132 1
    public function telephone(FormModelInterface $formModel, string $attribute, array $config = []): Telephone
133
    {
134 1
        return $this->input(Telephone::class, $formModel, $attribute, $config);
135
    }
136
137 10
    public function text(FormModelInterface $formModel, string $attribute, array $config = []): Text
138
    {
139 10
        return $this->input(Text::class, $formModel, $attribute, $config);
140
    }
141
142 1
    public function textarea(FormModelInterface $formModel, string $attribute, array $config = []): Textarea
143
    {
144 1
        return $this->input(Textarea::class, $formModel, $attribute, $config);
145
    }
146
147 1
    public function url(FormModelInterface $formModel, string $attribute, array $config = []): Url
148
    {
149 1
        return $this->input(Url::class, $formModel, $attribute, $config);
150
    }
151
152 4
    public function label(FormModelInterface $formModel, string $attribute, array $config = []): Label
153
    {
154 4
        $widgetConfig = array_merge(
155 4
            $this->makeLabelConfig(),
156
            $config,
157
        );
158 4
        return Label::widget($widgetConfig)->attribute($formModel, $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\Part\Error or Yiisoft\Form\Field\Part\Hint or Yiisoft\Form\Field\Base\AbstractInputField. ( Ignorable by Annotation )

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

158
        return Label::widget($widgetConfig)->/** @scrutinizer ignore-call */ attribute($formModel, $attribute);
Loading history...
159
    }
160
161 3
    public function hint(FormModelInterface $formModel, string $attribute, array $config = []): Hint
162
    {
163 3
        $widgetConfig = array_merge(
164 3
            $this->hintConfig,
165
            $config,
166
        );
167 3
        return Hint::widget($widgetConfig)->attribute($formModel, $attribute);
168
    }
169
170 3
    public function error(FormModelInterface $formModel, string $attribute, array $config = []): Error
171
    {
172 3
        $widgetConfig = array_merge(
173 3
            $this->errorConfig,
174
            $config,
175
        );
176 3
        return Error::widget($widgetConfig)->attribute($formModel, $attribute);
177
    }
178
179
    /**
180
     * @psalm-template T
181
     * @psalm-param class-string<T> $class
182
     * @psalm-return T
183
     */
184 23
    public function input(string $class, FormModelInterface $formModel, string $attribute, array $config = []): object
185
    {
186 23
        $widget = $this->field($class, $config);
187 23
        if (!$widget instanceof AbstractInputField) {
188
            throw new InvalidArgumentException(
189
                sprintf(
190
                    'Input widget must be instance of "%s".',
191
                    AbstractInputField::class
192
                )
193
            );
194
        }
195
196 23
        return $widget->attribute($formModel, $attribute);
197
    }
198
199
    /**
200
     * @psalm-template T
201
     * @psalm-param class-string<T> $class
202
     * @psalm-return T
203
     */
204 27
    public function field(string $class, array $config = []): object
205
    {
206 27
        $traits = class_uses($class);
207 27
        if ($traits === false) {
208
            throw new RuntimeException('Invalid field class.');
209
        }
210
211 27
        $supports = [];
212 27
        if (in_array(PlaceholderTrait::class, $traits, true)) {
213 16
            $supports[] = self::SUPPORT_PLACEHOLDER;
214
        }
215
216 27
        $config = array_merge(
217 27
            $this->makeFieldConfig($supports),
218 27
            $this->fieldConfigs[$class] ?? [],
219
            $config,
220 27
            ['class' => $class],
221
        );
222
223
        /** @psalm-var T&AbstractField */
224 27
        return WidgetFactory::createWidget($config);
225
    }
226
227
    /**
228
     * @param int[] $supports
229
     */
230 27
    private function makeFieldConfig(array $supports): array
231
    {
232 27
        $config = $this->makeBaseFieldConfig();
233 27
        foreach ($supports as $support) {
234
            switch ($support) {
235 16
                case self::SUPPORT_PLACEHOLDER:
236 16
                    if ($this->usePlaceholder !== null) {
237 2
                        $config['usePlaceholder()'] = [$this->usePlaceholder];
238
                    }
239 16
                    break;
240
            }
241
        }
242 27
        return $config;
243
    }
244
245 27
    private function makeBaseFieldConfig(): array
246
    {
247 27
        if ($this->baseFieldConfig === null) {
248 10
            $this->baseFieldConfig = [];
249
250 10
            if ($this->containerTag !== null) {
251 2
                $this->baseFieldConfig['containerTag()'] = [$this->containerTag];
252
            }
253
254 10
            if ($this->containerTagAttributes !== []) {
255 2
                $this->baseFieldConfig['containerTagAttributes()'] = [$this->containerTagAttributes];
256
            }
257
258 10
            if ($this->useContainer !== null) {
259 1
                $this->baseFieldConfig['useContainer()'] = [$this->useContainer];
260
            }
261
262 10
            if ($this->template !== null) {
263 1
                $this->baseFieldConfig['template()'] = [$this->template];
264
            }
265
266 10
            if ($this->setInputIdAttribute !== null) {
267 1
                $this->baseFieldConfig['setInputIdAttribute()'] = [$this->setInputIdAttribute];
268
            }
269
270 10
            if ($this->inputTagAttributes !== []) {
271 2
                $this->baseFieldConfig['inputTagAttributes()'] = [$this->inputTagAttributes];
272
            }
273
274 10
            $labelConfig = $this->makeLabelConfig();
275 10
            if ($labelConfig !== []) {
276 2
                $this->baseFieldConfig['labelConfig()'] = [$labelConfig];
277
            }
278
279 10
            if ($this->hintConfig !== []) {
280 1
                $this->baseFieldConfig['hintConfig()'] = [$this->hintConfig];
281
            }
282
283 10
            if ($this->errorConfig !== []) {
284 1
                $this->baseFieldConfig['errorConfig()'] = [$this->errorConfig];
285
            }
286
        }
287 27
        return $this->baseFieldConfig;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->baseFieldConfig could return the type null which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
288
    }
289
290 14
    private function makeLabelConfig(): array
291
    {
292 14
        $config = [];
293
294 14
        if ($this->setInputIdAttribute === false) {
295 2
            $config['useInputIdAttribute()'] = [false];
296
        }
297
298 14
        return array_merge($config, $this->labelConfig);
299
    }
300
}
301