Test Failed
Pull Request — master (#19)
by Flo
04:22
created

AbstractFormBuilder::addValidators()   C

Complexity

Conditions 7
Paths 5

Size

Total Lines 34
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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