Completed
Push — master ( 768def...b15d59 )
by Shcherbak
9s
created

Form   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 434
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Test Coverage

Coverage 100%

Importance

Changes 11
Bugs 6 Features 1
Metric Value
wmc 46
c 11
b 6
f 1
lcom 1
cbo 11
dl 0
loc 434
ccs 115
cts 115
cp 1
rs 8.3999

27 Methods

Rating   Name   Duplication   Size   Complexity  
A setName() 0 5 1
A handle() 0 14 4
A getMethod() 0 7 2
A setMethod() 0 5 1
A cleanValidationFlag() 0 4 1
B isValid() 0 23 6
A getErrors() 0 3 1
A addError() 0 8 2
A isSubmitted() 0 3 1
A getUid() 0 7 2
A getElements() 0 3 1
A getElement() 0 6 2
A setElement() 0 5 1
A addElement() 0 9 2
A input() 0 7 1
A password() 0 7 1
A select() 0 7 1
A radioList() 0 7 1
A textarea() 0 7 1
A hidden() 0 8 1
A submit() 0 7 1
A checkboxList() 0 7 1
A render() 0 3 1
A renderElements() 0 17 4
B renderStart() 0 26 4
A renderEnd() 0 3 1
A checkbox() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like Form 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 Form, and based on these observations, apply Extract Interface, too.

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