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

AbstractFormBuilder::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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