Completed
Push — master ( 4d8439...98d3f4 )
by Derek Stephen
05:28 queued 11s
created

FieldAbstract   B

Complexity

Total Complexity 44

Size/Duplication

Total Lines 362
Duplicated Lines 0 %

Coupling/Cohesion

Components 4
Dependencies 9

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 44
lcom 4
cbo 9
dl 0
loc 362
ccs 110
cts 110
cp 1
rs 8.8798
c 0
b 0
f 0

36 Methods

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

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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Zend\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 49
    public function __construct($name, $value = null)
58
    {
59 49
        $this->required = false;
60 49
        $this->dynamicFormCollection = [];
61 49
        $this->filterCollection = new FilterCollection();
62 49
        $this->validatorCollection = new ValidatorCollection();
63 49
        $this->renderer = new TextRender();
64 49
        $this->setName($name);
65 49
        $this->addFilter(new FilterAdapterZf(new ToNull()));
66 49
        $value === null ? null : $this->setValue($value);
67 49
        $this->init();
68
    }
69
70
    /**
71
     * @return string
72 37
     */
73
    public function getName(): string
74 37
    {
75
        return $this->getAttribute('name');
76
    }
77
78
    /**
79
     * @param string $name
80
     */
81 49
    public function setName(string $name): void
82
    {
83 49
        $this->setAttribute('name', $name);
84 49
    }
85
86
    /**
87
     * @return string
88
     */
89
    public function getId(): ?string
90 29
    {
91
        return $this->getAttribute('id');
92 29
    }
93
94
    /**
95
     * @param string $id
96
     */
97
    public function setId(string $id): void
98
    {
99 8
        $this->setAttribute('id', $id);
100
    }
101 8
102 8
    /**
103
     * @return string
104
     */
105
    public function getClass(): string
106
    {
107
        return $this->getAttribute('class') ?: 'form-control';
108 1
    }
109
110 1
    /**
111
     * @param string $class
112
     * @return FieldAbstract
113
     */
114
    public function setClass(string $class): void
115
    {
116
        $this->setAttribute('class', $class);
117 8
    }
118
119 8
    /**
120 8
     * @return mixed
121
     */
122
    public function getValue()
123
    {
124
        return $this->getAttribute('value');
125
    }
126 36
127
    /**
128 36
     * @param string $value
129
     */
130
    public function setValue($value): void
131
    {
132
        $this->setAttribute('value', $value);
133
        $this->filterValue();
134
    }
135 29
136
    /**
137 29
     * @param ValidatorInterface $validator
138 29
     */
139 29
    public function addValidator(ValidatorInterface $validator): void
140
    {
141
        $this->validatorCollection->append($validator);
142
    }
143
144
    /**
145
     * @return ValidatorCollection
146 20
     */
147
    public function getValidators(): ValidatorCollection
148 20
    {
149 20
        return $this->validatorCollection;
150
    }
151
152
    /**
153
     * @param FilterInterface $filter
154
     */
155 2
    public function addFilter(FilterInterface $filter): void
156
    {
157 2
        $this->filterCollection->append($filter);
158
    }
159
160
    /**
161
     * @param FilterInterface $transformer
162
     */
163
    public function setTransformer(TransformerInterface $transformer): void
164 28
    {
165
        $this->transformer = $transformer;
0 ignored issues
show
Documentation Bug introduced by
It seems like $transformer of type object<Del\Form\Field\TransformerInterface> is incompatible with the declared type object<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...
166 28
    }
167 28
168
    /**
169
     * @return TransformerInterface
170
     */
171
    public function getTransformer(): TransformerInterface
172
    {
173 1
        return $this->transformer;
174
    }
175 1
176
    /**
177
     * @return bool
178
     */
179
    public function hasTransformer(): bool
180
    {
181
        return $this->transformer instanceof TransformerInterface;
182
    }
183
184 31
    /**
185
     * @return FilterCollection
186 31
     */
187 31
    public function getFilters(): FilterCollection
188 31
    {
189 17
        return $this->filterCollection;
190 17
    }
191
192 31
    /**
193 31
     *  Runs the checkForErrors method for each field, which adds to errorMessages if invalid
194
     *
195
     * @return bool
196
     * @throws Exception If validation of $value is impossible
197
     */
198
    public function isValid(): bool
199 17
    {
200
        $this->errorMessages = [];
201 17
        $this->validatorCollection->rewind();
202
203 17
        while ($this->validatorCollection->valid()) {
204 13
            $this->checkForErrors($this->validatorCollection->current());
205
            $this->validatorCollection->next();
206 17
        }
207
208 29
        $count = \count($this->errorMessages);
209
210 29
        return $count == 0;
211 29
    }
212 29
213 9
    /**
214 9
     * @param ValidatorInterface $validator
215
     */
216 29
    private function checkForErrors(ValidatorInterface $validator): void
217 29
    {
218 29
        $value = $this->getValue();
219
220
        if ((!$validator->isValid($value)) && $this->isRequired()) {
221
            $this->errorMessages = array_merge($this->errorMessages, $validator->getMessages());
222
        }
223 10
    }
224
225 10
    /**
226
     * @throws Exception
227
     */
228
    private function filterValue(): void
229
    {
230
        $value = $this->getAttribute('value');
231 28
        $this->filterCollection->rewind();
232
233 28
        while ($this->filterCollection->valid()) {
234
            $value = $this->filterCollection->current()->filter($value);
235
            $this->filterCollection->next();
236
        }
237
238
        $this->filterCollection->rewind();
239
        $this->setAttribute('value', $value);
240 17
    }
241
242 17
    /**
243 17
     * @return array
244
     */
245
    public function getMessages(): array
246
    {
247
        return array_values($this->errorMessages);
248
    }
249
250 5
    /**
251
     * @return string
252 5
     */
253 5
    public function getLabel(): ?string
254
    {
255
        return $this->label;
256
    }
257
258
    /**
259 4
     * @param string $label
260
     */
261 4
    public function setLabel(string $label): void
262
    {
263
        $this->label = $label;
264
    }
265
266
    /**
267 2
     * @param string $message
268
     */
269 2
    public function setCustomErrorMessage(string $message): void
270
    {
271
        $this->customErrorMessage = $message;
272
    }
273
274
    /**
275 29
     * @return bool
276
     */
277 29
    public function hasCustomErrorMessage(): bool
278
    {
279
        return $this->customErrorMessage != null;
280
    }
281
282
    /**
283
     * @return string
284 33
     */
285
    public function getCustomErrorMessage(): string
286 33
    {
287 33
        return $this->customErrorMessage;
288
    }
289
290
    /**
291
     * @return FieldRendererInterface
292
     */
293
    public function getRenderer(): FieldRendererInterface
294
    {
295
        return $this->renderer;
296 35
    }
297
298 35
    /**
299
     * @param FieldRendererInterface $renderer
300
     */
301
    public function setRenderer(FieldRendererInterface $renderer): void
302
    {
303
        $this->renderer = $renderer;
304
    }
305 17
306
    /**
307 17
     * If a field is required then it must have a value
308 17
     * We add a not empty validator
309 17
     *
310
     * @return boolean
311
     */
312 17
    public function isRequired(): bool
313
    {
314 17
        return $this->required;
315 17
    }
316 17
317
    /**
318 2
     * @param boolean $required
319
     */
320 2
    public function setRequired(bool $required): void
321 2
    {
322 2
        $required ? $this->addNotEmptyValidator() : $this->removeNotEmptyValidator();
323 2
        $this->required = $required;
324 2
    }
325 1
326 2
    /**
327
     * adds not empty validator
328 2
     */
329
    private function addNotEmptyValidator(): void
330
    {
331
        $notEmpty = new NotEmpty();
332
        $this->addValidator($notEmpty);
333
    }
334
335 3
    /**
336
     *  removes not empty validator
337 3
     */
338 3
    private function removeNotEmptyValidator(): void
339
    {
340
        $this->validatorCollection->rewind();
341
342
        while ($this->validatorCollection->valid()) {
343
            $validator = $this->validatorCollection->current();
344 35
            $validator instanceof NotEmpty
345
                ? $this->validatorCollection->offsetUnset($this->validatorCollection->key())
346 35
                : null;
347
            $this->validatorCollection->next();
348
        }
349
    }
350
351
    /**
352
     * @param FormInterface $form
353 4
     * @param string $triggerValue
354
     */
355 4
    public function addDynamicForm(FormInterface $form, string $triggerValue): void
356 1
    {
357
        $this->dynamicFormCollection[$triggerValue] = $form;
358 3
    }
359
360
    /**
361
     * @return bool
362
     */
363
    public function hasDynamicForms(): bool
364
    {
365
        return count($this->dynamicFormCollection) > 0;
366
    }
367
368
    /**
369
     * @return FormInterface[]
370
     * @throws Exception
371
     */
372
    public function getDynamicForms(): array
373
    {
374
        if (!$this->hasDynamicForms()) {
375
            throw new Exception('No dynamic form for this value - Did you check hasDynamicForm() ?');
376
        }
377
        return $this->dynamicFormCollection;
378
    }
379
}