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