Completed
Push — master ( 4d5898...2a5654 )
by Gabor
03:30
created

AbstractElement::isValid()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 6.9849

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 3
cts 7
cp 0.4286
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 2
nop 1
crap 6.9849
1
<?php
2
/**
3
 * WebHemi.
4
 *
5
 * PHP version 5.6
6
 *
7
 * @copyright 2012 - 2016 Gixx-web (http://www.gixx-web.com)
8
 * @license   https://opensource.org/licenses/MIT The MIT License (MIT)
9
 *
10
 * @link      http://www.gixx-web.com
11
 */
12
namespace WebHemi\Form\Element\Web;
13
14
use InvalidArgumentException;
15
use Iterator;
16
use RuntimeException;
17
use WebHemi\Form\Element\FormElementInterface;
18
use WebHemi\Form\Element\NestedElementInterface;
19
use WebHemi\Form\Element\Traits\IteratorTrait;
20
use WebHemi\Validator\ValidatorInterface;
21
22
/**
23
 * Class AbstractElement
24
 */
25
abstract class AbstractElement implements FormElementInterface, Iterator
26
{
27
    /** @var int */
28
    protected static $tabIndex = 1;
29
30
    /** @var string */
31
    protected $name;
32
    /** @var string */
33
    protected $label;
34
    /** @var mixed */
35
    protected $value;
36
    /** @var array */
37
    protected $attributes = [];
38
    /** @var array<ValidatorInterface> */
39
    protected $validators = [];
40
    /** @var array */
41
    protected $errors = [];
42
    /** @var FormElementInterface */
43
    protected $parentNode;
44
    /** @var array */
45
    protected $mandatoryParentTypes = [];
46
47
    // The implementation of the Iterator interface.
48
    use IteratorTrait;
49
50
    /**
51
     * AbstractFormElement constructor.
52
     *
53
     * @param string $name
54
     * @param string $label
55
     * @param mixed  $value
56
     */
57 5
    public function __construct($name = '', $label = '', $value = null)
58
    {
59 5
        $this->name = preg_replace('/[^a-z0-9]/', '_', strtolower($name));
60 5
        $this->label = $label;
61 5
        $this->value = $value;
62 5
    }
63
64
    /**
65
     * Returns the element type.
66
     *
67
     * @return string
68
     */
69
    abstract public function getType();
70
71
    /**
72
     * Sets element name. The implementation should decide if it is allowed after init.
73
     *
74
     * @param string $name
75
     * @return AbstractElement
76
     */
77 5
    public function setName($name)
78
    {
79 5
        $this->name = $name;
80
81 5
        return $this;
82
    }
83
84
    /**
85
     * Returns the element name. If parameter is TRUE, then the method should include all the parents' names as well.
86
     *
87
     * @param boolean $getFulNodeName
88
     * @return string
89
     */
90 2
    public function getName($getFulNodeName = true)
91
    {
92 2
        $name = $this->name;
93
94 2
        if ($getFulNodeName) {
95 2
            if ($this->parentNode instanceof FormElementInterface) {
96
                $name = $this->parentNode->getName().'['.$this->name.']';
97
            }
98
        }
99
100 2
        return $name;
101
    }
102
103
    /**
104
     * Sets element label.
105
     *
106
     * @param string $label
107
     * @return AbstractElement
108
     */
109
    public function setLabel($label)
110
    {
111
        $this->label = $label;
112
113
        return $this;
114
    }
115
116
    /**
117
     * Returns the element label.
118
     *
119
     * @return string
120
     */
121
    public function getLabel()
122
    {
123
        return $this->label;
124
    }
125
126
    /**
127
     * Sets element value.
128
     *
129
     * @param mixed $value
130
     * @return AbstractElement
131
     */
132
    public function setValue($value)
133
    {
134
        $this->value = $value;
135
136
        return $this;
137
    }
138
139
    /**
140
     * Returns element value.
141
     *
142
     * @return mixed
143
     */
144
    public function getValue()
145
    {
146
        return $this->value;
147
    }
148
149
    /**
150
     * Gets element Id.
151
     *
152
     * @return string
153
     */
154
    public function getId()
155
    {
156
        $name = $this->getName();
157
        $md5Match = [];
158
159
        // Rip off the unique form prefix to make possible to work with fixed CSS id selectors.
160
        if (preg_match('/^[-z0-9\_\-\[\]]+\_(?P<md5>[a-f0-9]{32}).*$/', $name, $md5Match)) {
161
            $name = str_replace('_'.$md5Match['md5'], '', $name);
162
        }
163
164
        $elementId = 'id_'.trim(preg_replace('/[^a-z0-9]/', '_', $name), '_');
165
166
        return str_replace('__', '_', $elementId);
167
    }
168
169
    /**
170
     * Sets multiple attributes.
171
     *
172
     * @param array $attributes
173
     * @return AbstractElement
174
     */
175 5
    public function setAttributes(array $attributes)
176
    {
177 5
        $this->attributes = [];
178
179 5
        foreach ($attributes as $key => $value) {
180 5
            $this->setAttribute($key, $value);
181
        }
182
183 5
        return $this;
184
    }
185
186
    /**
187
     * Sets element attribute.
188
     *
189
     * @param string $key
190
     * @param mixed $value
191
     * @throws InvalidArgumentException
192
     * @return AbstractElement
193
     */
194 5
    protected function setAttribute($key, $value)
195
    {
196 5
        if (!is_scalar($value)) {
197
            throw new InvalidArgumentException('Element attribute can hold scalar data only.');
198
        }
199
200 5
        $this->attributes[$key] = $value;
201
202 5
        return $this;
203
    }
204
205
    /**
206
     * Gets all the attributes.
207
     *
208
     * @return array
209
     */
210 3
    public function getAttributes()
211
    {
212 3
        return $this->attributes;
213
    }
214
215
    /**
216
     * Gets element attribute.
217
     *
218
     * @param string $name
219
     * @return mixed
220
     */
221
    public function getAttribute($name)
222
    {
223
        if (!isset($this->attributes[$name])) {
224
            throw new InvalidArgumentException(sprintf('Invalid attribute: `%s`', $name));
225
        }
226
227
        return $this->attributes[$name];
228
    }
229
230
    /**
231
     * Sets and increments the tabulator index globally. This method should be used only on visible elements.
232
     *
233
     * @return AbstractElement
234
     */
235
    public function setTabIndex()
236
    {
237
        $this->attributes['tabindex'] = self::$tabIndex++;
238
239
        return $this;
240
    }
241
242
    /**
243
     * Sets the element errors. Usually the validator should set it, but it is allowed to set from outside too.
244
     *
245
     * @param array $errors
246
     * @return AbstractElement
247
     */
248
    public function setErrors(array $errors)
249
    {
250
        $this->errors = $errors;
251
252
        return $this;
253
    }
254
255
    /**
256
     * Checks if there are error messages set.
257
     *
258
     * @return boolean
259
     */
260
    public function hasErrors()
261
    {
262
        return !empty($this->errors);
263
    }
264
265
    /**
266
     * Gets validation errors.
267
     *
268
     * @return array
269
     */
270
    public function getErrors()
271
    {
272
        return $this->errors;
273
    }
274
275
    /**
276
     * Sets the element validators.
277
     *
278
     * @param array<ValidatorInterface> $validators
279
     * @return FormElementInterface
280
     */
281
    public function setValidators(array $validators)
282
    {
283
        $this->validators = [];
284
285
        foreach ($validators as $validator) {
286
            $this->addValidator($validator);
287
        }
288
289
        return $this;
290
    }
291
292
    /**
293
     * Adds validator to the form.
294
     *
295
     * @param ValidatorInterface $validator
296
     * @return AbstractElement
297
     */
298
    protected function addValidator(ValidatorInterface $validator)
299
    {
300
        $this->validators[] = $validator;
301
302
        return $this;
303
    }
304
305
    /**
306
     * Gets the element validators.
307
     *
308
     * @return array<ValidatorInterface>
309
     */
310
    public function getValidators()
311
    {
312
        return $this->validators;
313
    }
314
315
    /**
316
     * Validates element value.
317
     *
318
     * @param bool $reValidate
319
     * @return bool
320
     */
321 1
    public function isValid($reValidate = false)
322
    {
323 1
        if ($reValidate) {
324
            $this->errors = [];
325
326
            /** @var ValidatorInterface $validator */
327
            foreach ($this->validators as $validator) {
328
                if (!$validator->validate($this->value)) {
329
                    $this->errors[] = $validator->getError();
330
                }
331
            }
332
        }
333
334 1
        return empty($this->errors);
335
    }
336
337
    /**
338
     * Sets the parent element.
339
     *
340
     * @param FormElementInterface $formElement
341
     * @throws RuntimeException
342
     * @return AbstractElement
343
     */
344
    public function setParentNode(FormElementInterface $formElement)
345
    {
346
        if (!$formElement instanceof NestedElementInterface) {
347
            throw new RuntimeException(
348
                sprintf(
349
                    'Cannot set `%s` as child element of `%s`.',
350
                    $this->getType(),
351
                    $formElement->getType()
352
                )
353
            );
354
        }
355
356
        $this->parentNode = $formElement;
357
358
        return $this;
359
    }
360
361
    /**
362
     * Gets the parent element.
363
     *
364
     * @return FormElementInterface
365
     */
366
    public function getParentNode()
367
    {
368
        return $this->parentNode;
369
    }
370
}
371