Completed
Pull Request — master (#28)
by Vitaliy
04:27
created

Form::setElement()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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