Completed
Pull Request — master (#28)
by Vitaliy
03:02
created

Form::addElement()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6666
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
3
4
  namespace Fiv\Form;
5
6
  use Fiv\Form\Element;
7
  use Fiv\Form\Element\Checkbox;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Fiv\Form\Checkbox.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
  use Fiv\Form\Element\CheckboxList;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Fiv\Form\CheckboxList.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
9
  use Fiv\Form\Element\Submit;
10
  use Fiv\Form\Element\TextArea;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Fiv\Form\TextArea.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
11
12
  /**
13
   * @author Ivan Shcherbak <[email protected]>
14
   */
15
  class Form extends Element\Html {
16
17
    /**
18
     * @var null|string
19
     */
20
    protected $uid = null;
21
22
    /**
23
     * @var boolean|null
24
     */
25
    protected $validationResult = null;
26
27
    /**
28
     * @var array
29
     */
30
    protected $errorList = [];
31
32
    /**
33
     * @var array|null
34
     */
35
    protected $data = null;
36
37
    /**
38
     * @var bool
39
     */
40
    protected $isSubmitted = false;
41
42
    /**
43
     * @var Element\BaseElement[]
44
     */
45
    protected $elements = [];
46
47
    /**
48
     * Default form attributes
49
     *
50
     * @var array
51
     */
52
    protected $attributes = [
53
      'method' => 'post',
54
    ];
55
56
57
    /**
58
     * @param $name
59
     * @return $this
60
     */
61 9
    public function setName($name) {
62 9
      $this->uid = $name;
63 9
      $this->attributes['name'] = $name;
64 9
      return $this;
65
    }
66
67
68
    /**
69
     * @param RequestContext $requestContext
70
     * @return $this
71
     */
72 1
    public function handleRequestContext(RequestContext $requestContext) {
73 1
      $this->cleanValidationFlag();
74 1
      foreach ($this->getElements() as $element) {
75 1
        $element->handleRequestContext($requestContext);
76
      }
77
78 1
      $this->data = $requestContext->all();
79 1
      $this->isSubmitted = false;
80 1
      if ($requestContext->isMethod($this->getMethod()) and $requestContext->has($this->getUid())) {
81 1
        $this->isSubmitted = true;
82
      }
83
84 1
      return $this;
85
    }
86
87
88
    /**
89
     * @return array
90
     * @throws \Exception
91
     */
92 2
    public function getData() {
93 2
      if ($this->data === null) {
94
        throw new \Exception('Data does not exist!');
95
      }
96
97 2
      return $this->data;
98
    }
99
100
101
    /**
102
     * @param array|\Iterator $data
103
     * @throws \Exception
104
     * @deprecated
105
     */
106 18
    public function setData($data) {
107 18
      if ($data instanceof \Iterator) {
108
        $data = iterator_to_array($data);
109
      }
110
111 18
      if (!is_array($data)) {
112 1
        throw new \Exception('Data should be an array');
113
      }
114
115
116 17
      $this->cleanValidationFlag();
117
118 17
      $formData = [];
119 17
      foreach ($this->elements as $element) {
120 16
        $element->clearValue();
121 16
        $name = $element->getName();
122
123 16
        if ($element instanceof Checkbox) {
124 2
          $data[$name] = array_key_exists($name, $data) ? 1 : 0;
125
        } elseif ($element instanceof CheckboxList) {
126
          $data[$name] = isset($data[$name]) ? $data[$name] : [];
127
        }
128
129 16
        if (array_key_exists($name, $data)) {
130 15
          $element->setValue($data[$name]);
131 16
          $formData[$name] = $element->getValue();
132
        }
133
134
      }
135
136 17
      $this->isSubmitted = isset($data[$this->getUid()]);
137 17
      $this->data = $formData;
138 17
    }
139
140
141
    /**
142
     * @return string
143
     */
144 5
    public function getMethod() {
145 5
      if (!empty($this->attributes['method'])) {
146 5
        return strtolower($this->attributes['method']);
147
      }
148
149 1
      return null;
150
    }
151
152
153
    /**
154
     * @param string $method
155
     * @return $this
156
     */
157 3
    public function setMethod($method) {
158 3
      $this->attributes['method'] = $method;
159
160 3
      return $this;
161
    }
162
163
164
    /**
165
     *
166
     */
167 28
    protected function cleanValidationFlag() {
168 28
      $this->errorList = [];
169 28
      $this->validationResult = null;
170 28
    }
171
172
173
    /**
174
     * Check if form is submitted and all elements are valid
175
     *
176
     * @return boolean
177
     */
178 12
    public function isValid() {
179 12
      if ($this->validationResult !== null) {
180 1
        return $this->validationResult;
181
      }
182
183 12
      if (!$this->isSubmitted()) {
184 2
        return false;
185
      }
186
187 11
      $this->validationResult = true;
188 11
      foreach ($this->elements as $element) {
189 11
        if (!$element->isValid()) {
190 6
          $this->errorList = array_merge($this->errorList, $element->getValidatorsErrors());
191 11
          $this->validationResult = false;
192
        }
193
      }
194
195 11
      return $this->validationResult;
196
    }
197
198
199
    /**
200
     * @return array
201
     */
202 2
    public function getErrors() {
203 2
      return $this->errorList;
204
    }
205
206
207
    /**
208
     * @param string $error
209
     * @return $this
210
     */
211 1
    protected function addError($error) {
212 1
      if (!is_string($error)) {
213
        throw new \InvalidArgumentException('Error should be a string, ' . gettype($error) . ' given.');
214
      }
215 1
      $this->validationResult = false;
216 1
      $this->errorList[] = $error;
217 1
      return $this;
218
    }
219
220
221
    /**
222
     * Check if form is submitted
223
     *
224
     * @return bool
225
     */
226 15
    public function isSubmitted() {
227 15
      return $this->isSubmitted;
228
    }
229
230
231
    /**
232
     * Return unique id of form
233
     *
234
     * @return string
235
     */
236 22
    public function getUid() {
237 22
      if (empty($this->uid)) {
238 17
        $this->uid = md5(get_called_class());
239
      }
240
241 22
      return $this->uid;
242
    }
243
244
245
    /**
246
     * @return Element\BaseElement[]
247
     */
248 8
    public function getElements() {
249 8
      return $this->elements;
250
    }
251
252
253
    /**
254
     * @param string $name
255
     * @return Element\BaseElement
256
     * @throws \InvalidArgumentException
257
     */
258 3
    public function getElement($name) {
259 3
      if (empty($this->elements[$name])) {
260 1
        throw new \InvalidArgumentException('Element with name "' . $name . '" not found');
261
      }
262 2
      return $this->elements[$name];
263
    }
264
265
266
    /**
267
     * @param string $name
268
     * @param string|null $text
269
     * @return \Fiv\Form\Element\Input
270
     */
271 13
    public function input($name, $text = null) {
272 13
      $input = new Element\Input();
273 13
      $input->setName($name);
274 13
      $input->setText($text);
275 13
      $this->addElement($input);
276 13
      return $input;
277
    }
278
279
280
    /**
281
     * @param string $name
282
     * @param string|null $text
283
     * @return \Fiv\Form\Element\Input
284
     */
285
    public function password($name, $text = null) {
286
      $input = new Element\Password();
287
      $input->setName($name);
288
      $input->setText($text);
289
      $this->addElement($input);
290
      return $input;
291
    }
292
293
294
    /**
295
     * @param string $name
296
     * @param null $text
297
     * @return Select
298
     */
299
    public function select($name, $text = null) {
300
      $select = new Element\Select();
301
      $select->setName($name);
302
      $select->setText($text);
303
      $this->addElement($select);
304
      return $select;
305
    }
306
307
308
    /**
309
     * @param string $name
310
     * @param string $text
311
     * @return RadioList
312
     */
313
    public function radioList($name, $text = null) {
314
      $radio = new Element\RadioList();
315
      $radio->setName($name);
316
      $radio->setText($text);
317
      $this->addElement($radio);
318
      return $radio;
319
    }
320
321
322
    /**
323
     * @param string $name
324
     * @param null $text
325
     * @return TextArea
326
     */
327 5
    public function textarea($name, $text = null) {
328 5
      $input = new TextArea();
329 5
      $input->setName($name);
330 5
      $input->setText($text);
331 5
      $this->addElement($input);
332 5
      return $input;
333
    }
334
335
336
    /**
337
     * ```
338
     * $form->hidden('key', md5($this-user->id . HASH_A);
339
     * ```
340
     * @param string $name
341
     * @param null $value
342
     * @return \Fiv\Form\Element\Input
343
     */
344 3
    public function hidden($name, $value = null) {
345 3
      $hidden = new  \Fiv\Form\Element\Input();
346 3
      $hidden->setType('hidden');
347 3
      $hidden->setName($name);
348 3
      $hidden->setValue($value);
349 3
      $this->addElement($hidden);
350 3
      return $hidden;
351
    }
352
353
354
    /**
355
     * ```
356
     * $form->submit('register', 'зареєструватись');
357
     * ```
358
     * @param string $name
359
     * @param null $value
360
     * @return Submit
361
     */
362 1
    public function submit($name, $value = null) {
363 1
      $input = new Submit();
364 1
      $input->setName($name);
365 1
      $input->setValue($value);
366 1
      $this->addElement($input);
367 1
      return $input;
368
    }
369
370
371
    /**
372
     * ```
373
     * $form->checkbox('subscribe', 'Підписка на новини');
374
     * ```
375
     * @param string $name
376
     * @param string|null $label
377
     * @return Checkbox
378
     */
379 2
    public function checkbox($name, $label = null) {
380 2
      $checkbox = new Checkbox();
381 2
      $checkbox->setName($name);
382 2
      $checkbox->setLabel($label);
383 2
      $this->addElement($checkbox);
384 2
      return $checkbox;
385
    }
386
387
388
    /**
389
     * @param string $name
390
     * @param null $text
391
     * @return CheckboxList
392
     */
393
    public function checkboxList($name, $text = null) {
394
      $checkbox = new CheckboxList();
395
      $checkbox->setName($name);
396
      $checkbox->setText($text);
397
      $this->addElement($checkbox);
398
      return $checkbox;
399
    }
400
401
402
    /**
403
     * Attach element to this form. Overwrite element with same name
404
     *
405
     * @param Element\BaseElement $element
406
     * @return $this
407
     */
408 2
    public function setElement(Element\BaseElement $element) {
409 2
      $this->cleanValidationFlag();
410 2
      $this->elements[$element->getName()] = $element;
411 2
      return $this;
412
    }
413
414
415
    /**
416
     * @param Element\BaseElement $element
417
     * @return $this
418
     * @throws \Exception
419
     */
420 25
    public function addElement(Element\BaseElement $element) {
421 25
      if (isset($this->elements[$element->getName()])) {
422 1
        throw new \Exception('Element with name ' . $element->getName() . ' is already added. Use setElement to overwrite it or change name');
423
      }
424
425 25
      $this->cleanValidationFlag();
426 25
      $this->elements[$element->getName()] = $element;
427 25
      return $this;
428
    }
429
430
431
    /**
432
     * Render full form
433
     *
434
     * @return string
435
     */
436 2
    public function render() {
437 2
      return $this->renderStart() . $this->renderElements() . $this->renderEnd();
438
    }
439
440
441
    /**
442
     * You can easy rewrite this method for custom design of your forms
443
     *
444
     * @return string
445
     */
446 2
    protected function renderElements() {
447 2
      $formHtml = '<dl>';
448
449 2
      foreach ($this->elements as $element) {
450
        # skip hidden element
451 1
        if ($element instanceof Element\Input and $element->getType() === 'hidden') {
452
          continue;
453
        }
454
455
        $formHtml .=
456 1
          '<dt>' . $element->getText() . '</dt>' .
457 1
          '<dd>' . $element->render() . '</dd>';
458
      }
459
460 2
      $formHtml .= '</dl>';
461 2
      return $formHtml;
462
    }
463
464
465
    /**
466
     * @return string
467
     */
468 3
    public function renderStart() {
469 3
      $hidden = new Element\Input();
470 3
      $hidden->setType('hidden');
471 3
      $hidden->addAttributes([
472 3
        'name' => $this->getUid(),
473
      ]);
474 3
      $hidden->setValue(1);
475
476
477
      # get default attribute
478 3
      $method = $this->getMethod();
479 3
      $this->setAttribute('method', $method);
480
481 3
      $html = '<form ' . Element\Html::renderAttributes($this->getAttributes()) . '>';
482 3
      $html .= $hidden->render();
483
484
      # render hidden element
485 3
      foreach ($this->elements as $element) {
486 2
        if ($element instanceof Element\Input and $element->getType() === 'hidden') {
487 2
          $html .= $element->render();
488
        }
489
      }
490
491
492 3
      return $html;
493
    }
494
495
496
    /**
497
     * @return string
498
     */
499 3
    public function renderEnd() {
500 3
      return '</form>';
501
    }
502
  }