Passed
Push — master ( acf3f9...98cfe3 )
by Derek Stephen
03:04
created

FieldAbstract   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 358
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 3
Bugs 1 Features 0
Metric Value
wmc 45
eloc 77
c 3
b 1
f 0
dl 0
loc 358
ccs 117
cts 117
cp 1
rs 8.8

34 Methods

Rating   Name   Duplication   Size   Complexity  
A setLabel() 0 3 1
A checkForErrors() 0 6 3
A getValue() 0 3 1
A addNotEmptyValidator() 0 4 1
A getValidators() 0 3 1
A getTransformer() 0 3 1
A setId() 0 3 1
A getMessages() 0 3 2
A getFilters() 0 3 1
A addFilter() 0 3 1
A setRequired() 0 4 2
A addDynamicForm() 0 3 1
A setValue() 0 4 1
A isRequired() 0 3 1
A hasCustomErrorMessage() 0 3 1
A getDynamicForms() 0 6 2
A getId() 0 3 1
A setTransformer() 0 3 1
A setRenderer() 0 3 1
A addValidator() 0 3 1
A getClass() 0 3 2
A getName() 0 3 1
A setName() 0 3 1
A hasTransformer() 0 3 1
A getLabel() 0 3 1
A setCustomErrorMessage() 0 3 1
A removeNotEmptyValidator() 0 10 3
A setClass() 0 3 1
A hasDynamicForms() 0 3 1
A filterValue() 0 12 2
A getRenderer() 0 3 1
A isValid() 0 13 2
A __construct() 0 10 2
A getCustomErrorMessage() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like FieldAbstract 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 FieldAbstract, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Del\Form\Field;
4
5
use Del\Form\Collection\FilterCollection;
6
use Del\Form\Collection\ValidatorCollection;
7
use Del\Form\Filter\Adapter\FilterAdapterZf;
8
use Del\Form\Filter\FilterInterface;
9
use Del\Form\FormInterface;
10
use Del\Form\Renderer\Field\FieldRendererInterface;
11
use Del\Form\Renderer\Field\TextRender;
12
use Del\Form\Traits\HasAttributesTrait;
13
use Del\Form\Validator\NotEmpty;
14
use Del\Form\Validator\ValidatorInterface;
15
use Exception;
16
use Laminas\Filter\ToNull;
17
18
abstract class FieldAbstract implements FieldInterface
19
{
20
21
    /**  @var FormInterface[] $dynamicFormCollection */
22
    private $dynamicFormCollection;
23
24
    /**  @var FilterCollection $filterCollection */
25
    private $filterCollection;
26
27
    /**  @var ValidatorCollection $validatorCollection */
28
    private $validatorCollection;
29
30
    /**  @var ValidatorCollection $validatorCollection */
31
    private $transformer;
32
33
    /** @var FieldRendererInterface $renderer  */
34
    private $renderer;
35
36
    /** @var array $errorMessages */
37
    private $errorMessages;
38
39
    /** @var string $customErrorMessage */
40
    private $customErrorMessage;
41
42
    /** @var string $label */
43
    private $label;
44
45
    /** @var bool $required */
46
    private $required;
47
48
    use HasAttributesTrait;
49
50
    /**
51
     * @return string
52
     */
53
    abstract public function getTag(): string;
54
55
    abstract public function init();
56
57 64
    public function __construct($name, $value = null)
58
    {
59 64
        $this->required = false;
60 64
        $this->dynamicFormCollection = [];
61 64
        $this->filterCollection = new FilterCollection();
62 64
        $this->validatorCollection = new ValidatorCollection();
63 64
        $this->renderer = new TextRender();
64 64
        $this->setName($name);
65 64
        $value === null ? null : $this->setValue($value);
66 64
        $this->init();
67 64
    }
68
69
    /**
70
     * @return string
71
     */
72 50
    public function getName(): string
73
    {
74 50
        return $this->getAttribute('name');
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getAttribute('name') could return the type null which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
75
    }
76
77
    /**
78
     * @param string $name
79
     */
80 64
    public function setName(string $name): void
81
    {
82 64
        $this->setAttribute('name', $name);
83 64
    }
84
85
    /**
86
     * @return string
87
     */
88 35
    public function getId(): ?string
89
    {
90 35
        return $this->getAttribute('id');
91
    }
92
93
    /**
94
     * @param string $id
95
     */
96 8
    public function setId(string $id): void
97
    {
98 8
        $this->setAttribute('id', $id);
99 8
    }
100
101
    /**
102
     * @return string
103
     */
104 1
    public function getClass(): string
105
    {
106 1
        return $this->getAttribute('class') ?: 'form-control';
107
    }
108
109
    /**
110
     * @param string $class
111
     * @return FieldAbstract
112
     */
113 10
    public function setClass(string $class): void
114
    {
115 10
        $this->setAttribute('class', $class);
116 10
    }
117
118
    /**
119
     * @return mixed
120
     */
121 49
    public function getValue()
122
    {
123 49
        return $this->getAttribute('value');
124
    }
125
126
    /**
127
     * @param string $value
128
     */
129 40
    public function setValue($value): void
130
    {
131 40
        $this->setAttribute('value', $value);
132 40
        $this->filterValue();
133 40
    }
134
135
    /**
136
     * @param ValidatorInterface $validator
137
     */
138 25
    public function addValidator(ValidatorInterface $validator): void
139
    {
140 25
        $this->validatorCollection->append($validator);
141 25
    }
142
143
    /**
144
     * @return ValidatorCollection
145
     */
146 2
    public function getValidators(): ValidatorCollection
147
    {
148 2
        return $this->validatorCollection;
149
    }
150
151
    /**
152
     * @param FilterInterface $filter
153
     */
154 36
    public function addFilter(FilterInterface $filter): void
155
    {
156 36
        $this->filterCollection->append($filter);
157 36
    }
158
159
    /**
160
     * @param FilterInterface $transformer
161
     */
162 2
    public function setTransformer(TransformerInterface $transformer): void
163
    {
164 2
        $this->transformer = $transformer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $transformer of type Del\Form\Field\TransformerInterface is incompatible with the declared type Del\Form\Collection\ValidatorCollection of property $transformer.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
165 2
    }
166
167
    /**
168
     * @return TransformerInterface
169
     */
170 2
    public function getTransformer(): TransformerInterface
171
    {
172 2
        return $this->transformer;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->transformer returns the type Del\Form\Collection\ValidatorCollection which is incompatible with the type-hinted return Del\Form\Field\TransformerInterface.
Loading history...
173
    }
174
175
    /**
176
     * @return bool
177
     */
178 12
    public function hasTransformer(): bool
179
    {
180 12
        return $this->transformer instanceof TransformerInterface;
181
    }
182
183
    /**
184
     * @return FilterCollection
185
     */
186 1
    public function getFilters(): FilterCollection
187
    {
188 1
        return $this->filterCollection;
189
    }
190
191
    /**
192
     *  Runs the checkForErrors method for each field, which adds to errorMessages if invalid
193
     *
194
     * @return bool
195
     */
196 42
    public function isValid(): bool
197
    {
198 42
        $this->errorMessages = [];
199 42
        $this->validatorCollection->rewind();
200
201 42
        while ($this->validatorCollection->valid()) {
202 22
            $this->checkForErrors($this->validatorCollection->current());
203 22
            $this->validatorCollection->next();
204
        }
205
206 42
        $count = \count($this->errorMessages);
207
208 42
        return $count == 0;
209
    }
210
211
    /**
212
     * @param ValidatorInterface $validator
213
     */
214 22
    private function checkForErrors(ValidatorInterface $validator): void
215
    {
216 22
        $value = $this->getValue();
217
218 22
        if ((!$validator->isValid($value)) && $this->isRequired()) {
219 18
            $this->errorMessages = array_merge($this->errorMessages, $validator->getMessages());
220
        }
221 22
    }
222
223
    /**
224
     * @throws Exception
225
     */
226 40
    private function filterValue(): void
227
    {
228 40
        $value = $this->getAttribute('value');
229 40
        $this->filterCollection->rewind();
230
231 40
        while ($this->filterCollection->valid()) {
232 15
            $value = $this->filterCollection->current()->filter($value);
233 15
            $this->filterCollection->next();
234
        }
235
236 40
        $this->filterCollection->rewind();
237 40
        $this->setAttribute('value', $value);
238 40
    }
239
240
    /**
241
     * @return array
242
     */
243 14
    public function getMessages(): array
244
    {
245 14
        return isset($this->customErrorMessage) ? [$this->customErrorMessage] : array_values($this->errorMessages);
246
    }
247
248
    /**
249
     * @return string
250
     */
251 34
    public function getLabel(): ?string
252
    {
253 34
        return $this->label;
254
    }
255
256
    /**
257
     * @param string $label
258
     */
259 20
    public function setLabel(string $label): void
260
    {
261 20
        $this->label = $label;
262 20
    }
263
264
    /**
265
     * @param string $message
266
     */
267 6
    public function setCustomErrorMessage(string $message): void
268
    {
269 6
        $this->customErrorMessage = $message;
270 6
    }
271
272
    /**
273
     * @return bool
274
     */
275 4
    public function hasCustomErrorMessage(): bool
276
    {
277 4
        return $this->customErrorMessage != null;
278
    }
279
280
    /**
281
     * @return string
282
     */
283 2
    public function getCustomErrorMessage(): string
284
    {
285 2
        return $this->customErrorMessage;
286
    }
287
288
    /**
289
     * @return FieldRendererInterface
290
     */
291 35
    public function getRenderer(): FieldRendererInterface
292
    {
293 35
        return $this->renderer;
294
    }
295
296
    /**
297
     * @param FieldRendererInterface $renderer
298
     */
299 41
    public function setRenderer(FieldRendererInterface $renderer): void
300
    {
301 41
        $this->renderer = $renderer;
302 41
    }
303
304
    /**
305
     * If a field is required then it must have a value
306
     * We add a not empty validator
307
     *
308
     * @return boolean
309
     */
310 46
    public function isRequired(): bool
311
    {
312 46
        return $this->required;
313
    }
314
315
    /**
316
     * @param boolean $required
317
     */
318 22
    public function setRequired(bool $required): void
319
    {
320 22
        $required ? $this->addNotEmptyValidator() : $this->removeNotEmptyValidator();
321 22
        $this->required = $required;
322 22
    }
323
324
    /**
325
     * adds not empty validator
326
     */
327 22
    private function addNotEmptyValidator(): void
328
    {
329 22
        $notEmpty = new NotEmpty();
330 22
        $this->addValidator($notEmpty);
331 22
    }
332
333
    /**
334
     *  removes not empty validator
335
     */
336 2
    private function removeNotEmptyValidator(): void
337
    {
338 2
        $this->validatorCollection->rewind();
339
340 2
        while ($this->validatorCollection->valid()) {
341 2
            $validator = $this->validatorCollection->current();
342 2
            $validator instanceof NotEmpty
343 2
                ? $this->validatorCollection->offsetUnset($this->validatorCollection->key())
0 ignored issues
show
Bug introduced by
It seems like $this->validatorCollection->key() can also be of type true; however, parameter $key of ArrayIterator::offsetUnset() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

343
                ? $this->validatorCollection->offsetUnset(/** @scrutinizer ignore-type */ $this->validatorCollection->key())
Loading history...
344 1
                : null;
345 2
            $this->validatorCollection->next();
346
        }
347 2
    }
348
349
    /**
350
     * @param FormInterface $form
351
     * @param string $triggerValue
352
     */
353 3
    public function addDynamicForm(FormInterface $form, string $triggerValue): void
354
    {
355 3
        $this->dynamicFormCollection[$triggerValue] = $form;
356 3
    }
357
358
    /**
359
     * @return bool
360
     */
361 47
    public function hasDynamicForms(): bool
362
    {
363 47
        return count($this->dynamicFormCollection) > 0;
364
    }
365
366
    /**
367
     * @return FormInterface[]
368
     * @throws Exception
369
     */
370 4
    public function getDynamicForms(): array
371
    {
372 4
        if (!$this->hasDynamicForms()) {
373 1
            throw new Exception('No dynamic form for this value - Did you check hasDynamicForm() ?');
374
        }
375 3
        return $this->dynamicFormCollection;
376
    }
377
}