Test Failed
Pull Request — master (#19)
by Flo
03:14
created

AbstractFormBuilder::add()   D

Complexity

Conditions 10
Paths 17

Size

Total Lines 46
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 46
rs 4.983
c 0
b 0
f 0
cc 10
eloc 24
nc 17
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Class AbstractFormBuilder | AbstractFormBuilder.php
4
 * @package Faulancer\Form\Type
5
 * @author Florian Knapp <[email protected]>
6
 */
7
namespace Faulancer\Form;
8
9
use Faulancer\Exception\FormInvalidException;
10
use Faulancer\Exception\InvalidArgumentException;
11
use Faulancer\Exception\InvalidFormElementException;
12
use Faulancer\Form\Type\AbstractType;
13
use Faulancer\Http\Request;
14
use Faulancer\ORM\Entity;
15
use Faulancer\Service\RequestService;
16
use Faulancer\ServiceLocator\ServiceLocator;
17
use Faulancer\Form\Validator\ValidatorChain;
18
19
/**
20
 * Class AbstractFormBuilder
21
 */
22
abstract class AbstractFormBuilder
23
{
24
25
    /** @var array */
26
    protected $formAttributes = [];
27
28
    /** @var AbstractType[] */
29
    protected $fields = [];
30
31
    /** @var Entity */
32
    protected $entity = null;
33
34
    /** @var string */
35
    protected $formErrorContainerPrefix = '';
36
37
    /** @var string */
38
    protected $formErrorContainerSuffix = '';
39
40
    /** @var string */
41
    protected $formErrorItemContainerPrefix = '';
42
43
    /** @var string */
44
    protected $formErrorItemContainerSuffix = '';
45
46
    /** @var  */
47
    private $confirmValue = null;
48
49
    /**
50
     * AbstractFormBuilder constructor
51
     * @param Entity $entity
52
     * @codeCoverageIgnore
53
     */
54
    public function __construct(Entity $entity = null)
55
    {
56
        if ($entity !== null) {
57
            $this->create();
58
            $this->setData($entity->getDataAsArray());
59
        }
60
61
        $this->create();
62
    }
63
64
    /**
65
     * @param string $name
66
     * @return AbstractType
67
     * @throws InvalidFormElementException
68
     * @codeCoverageIgnore
69
     */
70
    public function getField(string $name)
71
    {
72
        if (empty($this->fields[$name])) {
73
            throw new InvalidFormElementException('No field with name \'' . $name . '\' found');
74
        }
75
        return $this->fields[$name];
76
    }
77
78
    /**
79
     * @param array $attributes
80
     */
81
    public function setFormAttributes(array $attributes)
82
    {
83
        $this->formAttributes = $attributes;
84
    }
85
86
    /**
87
     * @return string
88
     */
89
    public function getFormOpen()
90
    {
91
        return '<form action="' . $this->formAttributes['action'] . '" method="' . $this->formAttributes['method'] . '">';
92
    }
93
94
    /**
95
     * @return string
96
     */
97
    public function getFormClose()
98
    {
99
        return '</form>';
100
    }
101
102
    /**
103
     * @param string $prefix
104
     * @param string $suffix
105
     * @codeCoverageIgnore
106
     */
107
    public function setFormErrorContainer(string $prefix, string $suffix)
108
    {
109
        $this->formErrorContainerPrefix = $prefix;
110
        $this->formErrorContainerSuffix = $suffix;
111
    }
112
113
    /**
114
     * @param string $prefix
115
     * @param string $suffix
116
     * @codeCoverageIgnore
117
     */
118
    public function setFormErrorItemContainer(string $prefix, string $suffix)
119
    {
120
        $this->formErrorItemContainerPrefix = $prefix;
121
        $this->formErrorItemContainerSuffix = $suffix;
122
    }
123
124
    /**
125
     * @return array
126
     * @codeCoverageIgnore
127
     */
128
    public function getData()
129
    {
130
        $result = [];
131
132
        /** @var AbstractType $value */
133
        foreach ($this->fields as $key => $field) {
134
135
            if ($field->getType() === 'submit' || empty($field->getValue())) {
136
                continue;
137
            }
138
139
            // Check stored confirm value (i.e. for password repeat requests) and ignore the second field
140
            if ($field->getValue() === $this->confirmValue) {
141
                continue;
142
            }
143
144
            $this->confirmValue = $field->getValue();
145
146
            $result[$key] = $field->getValue();
147
148
        }
149
150
        return $result;
151
    }
152
153
    /**
154
     * @param array $data
155
     * @codeCoverageIgnore
156
     */
157
    public function setData(array $data)
158
    {
159
        foreach ($data as $key => $value) {
160
161
            if (empty($this->fields[$key])) {
162
                continue;
163
            }
164
165
            $this->fields[$key]->setValue($value);
166
167
        }
168
    }
169
170
    /**
171
     * @return bool
172
     * @codeCoverageIgnore
173
     */
174
    public function isValid() :bool
175
    {
176
        $errors = [];
177
178
        /** @var AbstractType $field */
179
        foreach ($this->fields as $field) {
180
181
            $result = $field->isValid();
182
183
            if ($result !== null) {
184
                $errors[] = $result;
185
            }
186
187
        }
188
189
        return !in_array(false, $errors, true);
190
    }
191
192
    /**
193
     * @param array $definition
194
     * @throws InvalidArgumentException
195
     * @codeCoverageIgnore
196
     */
197
    protected function add(array $definition)
198
    {
199
        $type = $definition['attributes']['type'];
200
        $name = $definition['attributes']['name'];
201
202
        $namespace = '\Faulancer\Form\Type\Base\\' . ucfirst($type);
203
204
        if (!class_exists($namespace)) {
205
            throw new InvalidArgumentException('Requesting non existent form type ' . ucfirst($type));
206
        }
207
208
        $formErrorDecoration = [
209
            'containerPrefix'     => $this->formErrorContainerPrefix,
210
            'containerSuffix'     => $this->formErrorContainerSuffix,
211
            'containerItemPrefix' => $this->formErrorItemContainerPrefix,
212
            'containerItemSuffix' => $this->formErrorItemContainerSuffix
213
        ];
214
215
        /** @var AbstractType $typeClass */
216
        $typeClass = new $namespace($definition, $formErrorDecoration);
217
218
        $typeClass->setName($name);
219
        $typeClass->setType($type);
220
221
        if (!empty($this->fields[$name]) && !empty($this->fields[$name]->getValue())) {
222
            $typeClass->setValue($this->fields[$name]->getValue());
223
        }
224
225
        /** @var Request $request */
226
        $request  = ServiceLocator::instance()->get(RequestService::class);
227
        $postData = $request->getPostData();
228
229
        if ($request->isPost() && !empty($postData[$name])) {
230
            $typeClass->setValue($postData[$name]);
231
        }
232
233
        // If radio or checkbox field isn't selected, the field wouldn't
234
        // be send within post data so always add a validator if exists
235
        $isRadioOrCheckbox = $type === 'radio' || $type === 'checkbox';
236
237
        if ($isRadioOrCheckbox || ($request->isPost() && in_array($name, array_keys($postData)))) {
238
            $this->addValidators($typeClass, $definition);
239
        }
240
241
        $this->fields[$name] = $typeClass->create();
242
    }
243
244
    /**
245
     * @param AbstractType $typeClass
246
     * @param array        $definition
247
     * @throws FormInvalidException
248
     * @return boolean
249
     * @codeCoverageIgnore
250
     */
251
    private function addValidators(AbstractType &$typeClass, array $definition)
252
    {
253
        if (!empty($definition['validator'])) {
254
255
            $validatorChain = new ValidatorChain($typeClass);
256
257
            foreach ($definition['validator'] as $validator) {
258
259
                if (!class_exists($validator)) {
260
                    throw new FormInvalidException('Validator "' . $validator . '" doesn\'t exists');
261
                }
262
263
                $validatorChain->add(new $validator($typeClass));
264
265
            }
266
267
            $typeClass->setValidatorChain($validatorChain);
268
269
        } else {
270
271
            $validator = '\Faulancer\Form\Validator\Base\\' . ucfirst($typeClass->getType());
272
273
            if (empty($definition['validator']) && class_exists($validator)) {
274
                $typeClass->setDefaultValidator(new $validator($typeClass));
275
            }
276
277
        }
278
279
        return true;
280
    }
281
282
    /**
283
     * @return mixed
284
     */
285
    abstract protected function create();
286
287
}