Completed
Pull Request — master (#19)
by Flo
04:38
created

AbstractFormBuilder   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Importance

Changes 0
Metric Value
wmc 36
lcom 2
cbo 8
dl 0
loc 257
rs 8.8
c 0
b 0
f 0

13 Methods

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