Test Failed
Pull Request — master (#19)
by Flo
04:39
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
        $action = $this->formAttributes['action'] ?? '';
92
        $method = $this->formAttributes['method'] ?? '';
93
        $enctype = $this->formAttributes['enctype'] ?? 'application/x-www-form-urlencoded';
94
95
        return '<form action="' . $action . '" method="' . $method . '" enctype="' . $enctype . '">';
96
    }
97
98
    /**
99
     * @return string
100
     */
101
    public function getFormClose()
102
    {
103
        return '</form>';
104
    }
105
106
    /**
107
     * @param string $prefix
108
     * @param string $suffix
109
     * @codeCoverageIgnore
110
     */
111
    public function setFormErrorContainer(string $prefix, string $suffix)
112
    {
113
        $this->formErrorContainerPrefix = $prefix;
114
        $this->formErrorContainerSuffix = $suffix;
115
    }
116
117
    /**
118
     * @param string $prefix
119
     * @param string $suffix
120
     * @codeCoverageIgnore
121
     */
122
    public function setFormErrorItemContainer(string $prefix, string $suffix)
123
    {
124
        $this->formErrorItemContainerPrefix = $prefix;
125
        $this->formErrorItemContainerSuffix = $suffix;
126
    }
127
128
    /**
129
     * @return array
130
     * @codeCoverageIgnore
131
     */
132
    public function getData()
133
    {
134
        $result = [];
135
136
        /** @var AbstractType $value */
137
        foreach ($this->fields as $key => $field) {
138
139
            if ($field->getType() === 'submit' || empty($field->getValue())) {
140
                continue;
141
            }
142
143
            // Check stored confirm value (i.e. for password repeat requests) and ignore the second field
144
            if ($field->getValue() === $this->confirmValue) {
145
                continue;
146
            }
147
148
            $this->confirmValue = $field->getValue();
149
150
            $result[$key] = $field->getValue();
151
152
        }
153
154
        return $result;
155
    }
156
157
    /**
158
     * @param array $data
159
     * @codeCoverageIgnore
160
     */
161
    public function setData(array $data)
162
    {
163
        foreach ($data as $key => $value) {
164
165
            if (empty($this->fields[$key])) {
166
                continue;
167
            }
168
169
            $this->fields[$key]->setValue($value);
170
171
        }
172
    }
173
174
    /**
175
     * @return bool
176
     * @codeCoverageIgnore
177
     */
178
    public function isValid() :bool
179
    {
180
        $errors = [];
181
182
        /** @var AbstractType $field */
183
        foreach ($this->fields as $field) {
184
185
            $result = $field->isValid();
186
187
            if ($result !== null) {
188
                $errors[] = $result;
189
            }
190
191
        }
192
193
        return !in_array(false, $errors, true);
194
    }
195
196
    /**
197
     * @param array $definition
198
     * @throws InvalidArgumentException
199
     * @codeCoverageIgnore
200
     */
201
    protected function add(array $definition)
202
    {
203
        $type = $definition['attributes']['type'];
204
        $name = $definition['attributes']['name'];
205
206
        $namespace = '\Faulancer\Form\Type\Base\\' . ucfirst($type);
207
208
        if (!class_exists($namespace)) {
209
            throw new InvalidArgumentException('Requesting non existent form type ' . ucfirst($type));
210
        }
211
212
        $formErrorDecoration = [
213
            'containerPrefix'     => $this->formErrorContainerPrefix,
214
            'containerSuffix'     => $this->formErrorContainerSuffix,
215
            'containerItemPrefix' => $this->formErrorItemContainerPrefix,
216
            'containerItemSuffix' => $this->formErrorItemContainerSuffix
217
        ];
218
219
        /** @var AbstractType $typeClass */
220
        $typeClass = new $namespace($definition, $formErrorDecoration);
221
222
        $typeClass->setName($name);
223
        $typeClass->setType($type);
224
225
        if (!empty($this->fields[$name]) && !empty($this->fields[$name]->getValue())) {
226
            $typeClass->setValue($this->fields[$name]->getValue());
227
        }
228
229
        /** @var Request $request */
230
        $request  = ServiceLocator::instance()->get(RequestService::class);
231
        $postData = $request->getPostData();
232
233
        if ($request->isPost() && !empty($postData[$name])) {
234
            $typeClass->setValue($postData[$name]);
235
        }
236
237
        // If radio or checkbox field isn't selected, the field wouldn't
238
        // be send within post data so always add a validator if exists
239
        $isRadioOrCheckbox = $type === 'radio' || $type === 'checkbox';
240
241
        if ($isRadioOrCheckbox || ($request->isPost() && in_array($name, array_keys($postData)))) {
242
            $this->addValidators($typeClass, $definition);
243
        }
244
245
        $this->fields[$name] = $typeClass->create();
246
    }
247
248
    /**
249
     * @param AbstractType $typeClass
250
     * @param array        $definition
251
     * @throws FormInvalidException
252
     * @return boolean
253
     * @codeCoverageIgnore
254
     */
255
    private function addValidators(AbstractType &$typeClass, array $definition)
256
    {
257
        if (!empty($definition['validator'])) {
258
259
            $validatorChain = new ValidatorChain($typeClass);
260
261
            foreach ($definition['validator'] as $validator) {
262
263
                if (!class_exists($validator)) {
264
                    throw new FormInvalidException('Validator "' . $validator . '" doesn\'t exists');
265
                }
266
267
                $validatorChain->add(new $validator($typeClass));
268
269
            }
270
271
            $typeClass->setValidatorChain($validatorChain);
272
273
        } else {
274
275
            $validator = '\Faulancer\Form\Validator\Base\\' . ucfirst($typeClass->getType());
276
277
            if (empty($definition['validator']) && class_exists($validator)) {
278
                $typeClass->setDefaultValidator(new $validator($typeClass));
279
            }
280
281
        }
282
283
        return true;
284
    }
285
286
    /**
287
     * @return mixed
288
     */
289
    abstract protected function create();
290
291
}