Passed
Pull Request — master (#192)
by Sergei
02:45
created

FieldFactory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 1
eloc 0
c 2
b 1
f 0
nc 1
nop 11
dl 0
loc 13
ccs 1
cts 1
cp 1
crap 1
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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

140
        return Label::widget($widgetConfig)->/** @scrutinizer ignore-call */ attribute($formModel, $attribute);
Loading history...
141
    }
142
143 3
    public function hint(FormModelInterface $formModel, string $attribute, array $config = []): Hint
144
    {
145 3
        $widgetConfig = array_merge(
146 3
            $this->hintConfig,
147
            $config,
148
        );
149 3
        return Hint::widget($widgetConfig)->attribute($formModel, $attribute);
150
    }
151
152 3
    public function error(FormModelInterface $formModel, string $attribute, array $config = []): Error
153
    {
154 3
        $widgetConfig = array_merge(
155 3
            $this->errorConfig,
156
            $config,
157
        );
158 3
        return Error::widget($widgetConfig)->attribute($formModel, $attribute);
159
    }
160
161
    /**
162
     * @psalm-template T
163
     * @psalm-param class-string<T> $class
164
     * @psalm-return T
165
     */
166 21
    public function input(string $class, FormModelInterface $formModel, string $attribute, array $config = []): object
167
    {
168 21
        $widget = $this->field($class, $config);
169 21
        if (!$widget instanceof AbstractInputField) {
170
            throw new InvalidArgumentException(
171
                sprintf(
172
                    'Input widget must be instance of "%s".',
173
                    AbstractInputField::class
174
                )
175
            );
176
        }
177
178 21
        return $widget->attribute($formModel, $attribute);
179
    }
180
181
    /**
182
     * @psalm-template T
183
     * @psalm-param class-string<T> $class
184
     * @psalm-return T
185
     */
186 24
    public function field(string $class, array $config = []): object
187
    {
188 24
        $traits = class_uses($class);
189 24
        if ($traits === false) {
190
            throw new RuntimeException('Invalid field class.');
191
        }
192
193 24
        $supports = [];
194 24
        if (in_array(PlaceholderTrait::class, $traits, true)) {
195 16
            $supports[] = self::SUPPORT_PLACEHOLDER;
196
        }
197
198 24
        $config = array_merge(
199 24
            $this->makeFieldConfig($supports),
200 24
            $this->fieldConfigs[$class] ?? [],
201
            $config,
202 24
            ['class' => $class],
203
        );
204
205
        /** @psalm-var T&AbstractField */
206 24
        return WidgetFactory::createWidget($config);
207
    }
208
209
    /**
210
     * @param int[] $supports
211
     */
212 24
    private function makeFieldConfig(array $supports): array
213
    {
214 24
        $config = $this->makeBaseFieldConfig();
215 24
        foreach ($supports as $support) {
216
            switch ($support) {
217 16
                case self::SUPPORT_PLACEHOLDER:
218 16
                    if ($this->usePlaceholder !== null) {
219 2
                        $config['usePlaceholder()'] = [$this->usePlaceholder];
220
                    }
221 16
                    break;
222
            }
223
        }
224 24
        return $config;
225
    }
226
227 24
    private function makeBaseFieldConfig(): array
228
    {
229 24
        if ($this->baseFieldConfig === null) {
230 10
            $this->baseFieldConfig = [];
231
232 10
            if ($this->containerTag !== null) {
233 2
                $this->baseFieldConfig['containerTag()'] = [$this->containerTag];
234
            }
235
236 10
            if ($this->containerTagAttributes !== []) {
237 2
                $this->baseFieldConfig['containerTagAttributes()'] = [$this->containerTagAttributes];
238
            }
239
240 10
            if ($this->useContainer !== null) {
241 1
                $this->baseFieldConfig['useContainer()'] = [$this->useContainer];
242
            }
243
244 10
            if ($this->template !== null) {
245 1
                $this->baseFieldConfig['template()'] = [$this->template];
246
            }
247
248 10
            if ($this->setInputIdAttribute !== null) {
249 1
                $this->baseFieldConfig['setInputIdAttribute()'] = [$this->setInputIdAttribute];
250
            }
251
252 10
            if ($this->inputTagAttributes !== []) {
253 2
                $this->baseFieldConfig['inputTagAttributes()'] = [$this->inputTagAttributes];
254
            }
255
256 10
            $labelConfig = $this->makeLabelConfig();
257 10
            if ($labelConfig !== []) {
258 2
                $this->baseFieldConfig['labelConfig()'] = [$labelConfig];
259
            }
260
261 10
            if ($this->hintConfig !== []) {
262 1
                $this->baseFieldConfig['hintConfig()'] = [$this->hintConfig];
263
            }
264
265 10
            if ($this->errorConfig !== []) {
266 1
                $this->baseFieldConfig['errorConfig()'] = [$this->errorConfig];
267
            }
268
        }
269 24
        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...
270
    }
271
272 14
    private function makeLabelConfig(): array
273
    {
274 14
        $config = [];
275
276 14
        if ($this->setInputIdAttribute === false) {
277 2
            $config['useInputIdAttribute()'] = [false];
278
        }
279
280 14
        return array_merge($config, $this->labelConfig);
281
    }
282
}
283