Completed
Branch dynamicfields (fd80d4)
by Derek Stephen
06:25
created

FieldAbstract::addDynamicForm()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 0
cts 3
cp 0
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
crap 2
1
<?php
2
/**
3
 * User: delboy1978uk
4
 * Date: 19/11/2016
5
 * Time: 21:41
6
 */
7
8
namespace Del\Form\Field;
9
10
use Del\Form\Collection\FilterCollection;
11
use Del\Form\Collection\ValidatorCollection;
12
use Del\Form\Filter\FilterInterface;
13
use Del\Form\FormInterface;
14
use Del\Form\Renderer\Field\FieldRendererInterface;
15
use Del\Form\Renderer\Field\TextRender;
16
use Del\Form\Traits\HasAttributesTrait;
17
use Del\Form\Validator\Adapter\ValidatorAdapterZf;
18
use Del\Form\Validator\NotEmpty;
19
use Del\Form\Validator\ValidatorInterface;
20
use Exception;
21
22
abstract class FieldAbstract implements FieldInterface
23
{
24
25
    /**  @var array $dynamicFormCollection */
26
    private $dynamicFormCollection;
27
28
    /**  @var FilterCollection $filterCollection */
29
    private $filterCollection;
30
31
    /**  @var ValidatorCollection $validatorCollection */
32
    private $validatorCollection;
33
34
    /** @var FieldRendererInterface $renderer  */
35
    private $renderer;
36
37
    /** @var array $errorMessages */
38
    private $errorMessages;
39
40
    /** @var string $customErrorMessage */
41
    private $customErrorMessage;
42
43
    /** @var string $label */
44
    private $label;
45
46
    /** @var bool $required */
47
    private $required;
48
49
    use HasAttributesTrait;
50
51
    /**
52
     * @return string
53
     */
54
    abstract public function getTag();
55
56
    abstract public function init();
57
58 42
    public function __construct($name, $value = null)
59
    {
60 42
        $this->required = false;
61 42
        $this->dynamicFieldCollection = [];
0 ignored issues
show
Bug introduced by
The property dynamicFieldCollection does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
62 42
        $this->filterCollection = new FilterCollection();
63 42
        $this->validatorCollection = new ValidatorCollection();
64 42
        $this->renderer = new TextRender();
65 42
        $this->setName($name);
66 42
        is_null($value) ? null : $this->setValue($value);
67 42
        $this->init();
68 42
    }
69
70
    /**
71
     * @return string
72
     */
73 28
    public function getName()
74
    {
75 28
        return $this->getAttribute('name');
76
    }
77
78
    /**
79
     * @param string $name
80
     * @return FieldAbstract
81
     */
82 42
    public function setName($name)
83
    {
84 42
        $this->setAttribute('name', $name);
85 42
        return $this;
86
    }
87
88
    /**
89
     * @return string
90
     */
91 27
    public function getId()
92
    {
93 27
        return $this->getAttribute('id');
94
    }
95
96
    /**
97
     * @param string $id
98
     * @return FieldAbstract
99
     */
100 8
    public function setId($id)
101
    {
102 8
        $this->setAttribute('id', $id);
103 8
        return $this;
104
    }
105
106
    /**
107
     * @return string
108
     */
109 1
    public function getClass()
110
    {
111 1
        return $this->getAttribute('class') ?: 'form-control';
112
    }
113
114
    /**
115
     * @param string $class
116
     * @return FieldAbstract
117
     */
118 5
    public function setClass($class)
119
    {
120 5
        $this->setAttribute('class', $class);
121 5
        return $this;
122
    }
123
124
    /**
125
     * @return mixed
126
     */
127 31
    public function getValue()
128
    {
129 31
        return $this->getAttribute('value');
130
    }
131
132
    /**
133
     * @param mixed $value
134
     * @return FieldAbstract
135
     */
136 25
    public function setValue($value)
137
    {
138 25
        $this->setAttribute('value', $value);
139 25
        $this->filterValue();
140 25
        return $this;
141
    }
142
143
    /**
144
     * @param ValidatorInterface $validator
145
     * @return $this
146
     */
147 15
    public function addValidator(ValidatorInterface $validator)
148
    {
149 15
        $this->validatorCollection->append($validator);
150 15
        return $this;
151
    }
152
153
    /**
154
     * @return ValidatorCollection
155
     */
156 1
    public function getValidators()
157
    {
158 1
        return $this->validatorCollection;
159
    }
160
161
    /**
162
     * @param FilterInterface $filter
163
     * @return $this
164
     */
165 22
    public function addFilter(FilterInterface $filter)
166
    {
167 22
        $this->filterCollection->append($filter);
168 22
        return $this;
169
    }
170
171
    /**
172
     * @return FilterCollection
173
     */
174 1
    public function getFilters()
175
    {
176 1
        return $this->filterCollection;
177
    }
178
179
    /**
180
     *  Runs the checkForErrors method for each field, which adds to errorMessages if invalid
181
     *
182
     * @return bool
183
     * @throws Exception If validation of $value is impossible
184
     */
185 27
    public function isValid()
186
    {
187 27
        $this->errorMessages = [];
188 27
        $this->validatorCollection->rewind();
189 27
        while ($this->validatorCollection->valid()) {
190 14
            $this->checkForErrors($this->validatorCollection->current());
191 14
            $this->validatorCollection->next();
192
        }
193 27
        $count = count($this->errorMessages);
194 27
        return $count == 0;
195
    }
196
197
    /**
198
     * @param ValidatorInterface $validator
199
     */
200 14
    private function checkForErrors(ValidatorInterface $validator)
201
    {
202 14
        $value = $this->getValue();
203
204 14
        if ( (!$validator->isValid($value)) && $this->isRequired()) {
205 10
            $this->errorMessages = array_merge($this->errorMessages, $validator->getMessages());
206
        }
207 14
    }
208
209 25
    private function filterValue()
210
    {
211 25
        $value = $this->getAttribute('value');
212 25
        $this->filterCollection->rewind();
213 25
        while ($this->filterCollection->valid()) {
214 7
            $value = $this->filterCollection->current()->filter($value);
215 7
            $this->filterCollection->next();
216
        }
217 25
        $this->filterCollection->rewind();
218 25
        $this->setAttribute('value', $value);
219 25
    }
220
221
    /**
222
     * @return array
223
     */
224 8
    public function getMessages()
225
    {
226 8
        return array_values($this->errorMessages);
227
    }
228
229
    /**
230
     * @return string
231
     */
232 26
    public function getLabel()
233
    {
234 26
        return $this->label;
235
    }
236
237
    /**
238
     * @param string $label
239
     * @return $this
240
     */
241 14
    public function setLabel($label)
242
    {
243 14
        $this->label = $label;
244 14
        return $this;
245
    }
246
247
    /**
248
     * @param string $message
249
     * @return $this
250
     */
251 2
    public function setCustomErrorMessage($message)
252
    {
253 2
        $this->customErrorMessage = $message;
254 2
        return $this;
255
    }
256
257
    /**
258
     * @return bool
259
     */
260 4
    public function hasCustomErrorMessage()
261
    {
262 4
        return $this->customErrorMessage != null;
263
    }
264
265
    /**
266
     * @return string
267
     */
268 2
    public function getCustomErrorMessage()
269
    {
270 2
        return $this->customErrorMessage;
271
    }
272
273
    /**
274
     * @return FieldRendererInterface
275
     */
276 27
    public function getRenderer()
277
    {
278 27
        return $this->renderer;
279
    }
280
281
    /**
282
     * @param FieldRendererInterface $renderer
283
     * @return $this
284
     */
285 29
    public function setRenderer(FieldRendererInterface $renderer)
286
    {
287 29
        $this->renderer = $renderer;
288 29
        return $this;
289
    }
290
291
    /**
292
     * If a field is required then it must have a value
293
     * We add a not empty validator
294
     *
295
     * @return boolean
296
     */
297 31
    public function isRequired()
298
    {
299 31
        return $this->required;
300
    }
301
302
    /**
303
     * @param boolean $required
304
     * @return FieldAbstract
305
     */
306 12
    public function setRequired($required)
307
    {
308 12
        $required ? $this->addNotEmptyValidator() : $this->removeNotEmptyValidator();
309 12
        $this->required = $required;
310 12
        return $this;
311
    }
312
313 12
    private function addNotEmptyValidator()
314
    {
315 12
        $notEmpty = new NotEmpty();
316 12
        $this->addValidator($notEmpty);
317 12
    }
318
319 1
    private function removeNotEmptyValidator()
320
    {
321 1
        $this->validatorCollection->rewind();
322 1
        while ($this->validatorCollection->valid()) {
323 1
            $validator = $this->validatorCollection->current();
324 1
            $validator instanceof NotEmpty
325 1
                ? $this->validatorCollection->offsetUnset($this->validatorCollection->key())
326
                : null;
327 1
            $this->validatorCollection->next();
328
        }
329 1
    }
330
331
    /**
332
     * @param FormInterface $form
333
     * @param $triggerValue
334
     * @return $this
335
     */
336
    public function addDynamicForm(FormInterface $form, $triggerValue)
337
    {
338
//        $fields = $form->getFields();
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
339
//        while ($fields->valid()) {
340
//            $field = $fields->current();
341
//            $style = $field->getAttribute('style');
342
//            $field->setAttribute('data-dynamic-field', $this->getName());
343
//            $field->setAttribute('data-dynamic-field-trigger-value', $triggerValue);
344
//            $field->setAttribute('style', 'display: none; '.$style);
345
//            $fields->next();
346
//        }
347
        $this->dynamicFormCollection[$triggerValue] = $form;
348
        return $this;
349
    }
350
351
    /**
352
     * @return bool
353
     */
354 20
    public function hasDynamicForms()
355
    {
356 20
        return count($this->dynamicFormCollection);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return count($this->dynamicFormCollection); (integer) is incompatible with the return type declared by the interface Del\Form\Field\FieldInterface::hasDynamicForms of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
357
    }
358
359
    /**
360
     * @return FormInterface[]
361
     * @throws Exception
362
     */
363
    public function getDynamicForms()
364
    {
365
        if (!$this->hasDynamicForms()) {
366
            throw new Exception('No dynamic form for this value - Did you check hasDynamicForm() ?');
367
        }
368
        return $this->dynamicFormCollection;
369
    }
370
}