Completed
Push — feature/version-2 ( 3ca533...64e802 )
by Romain
01:41
created

FormValidatorExecutor   C

Complexity

Total Complexity 40

Size/Duplication

Total Lines 311
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
wmc 40
lcom 1
cbo 17
dl 0
loc 311
rs 6.6773
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A applyBehaviours() 0 8 1
A checkFieldsActivation() 0 10 3
B checkFieldActivation() 0 22 5
A checkFieldValidatorActivation() 0 10 4
A validateFields() 0 14 4
B validateField() 0 24 6
B processFieldValidator() 0 34 4
A getResult() 0 4 1
A getFieldActivationProcessResult() 0 4 1
A getValidatorActivationProcessResult() 0 4 1
A fieldActivationHasBeenChecked() 0 4 1
A markFieldActivationAsChecked() 0 4 1
A fieldActivationIsBeingChecked() 0 4 1
A markFieldActivationCheckBegin() 0 4 1
A markFieldActivationCheckEnd() 0 4 1
A fieldWasValidated() 0 4 1
A markFieldAsValidated() 0 4 1
A getConditionProcessor() 0 4 1
A getPhpConditionDataObject() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like FormValidatorExecutor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FormValidatorExecutor, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * 2017 Romain CANON <[email protected]>
4
 *
5
 * This file is part of the TYPO3 FormZ project.
6
 * It is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU General Public License, either
8
 * version 3 of the License, or any later version.
9
 *
10
 * For the full copyright and license information, see:
11
 * http://www.gnu.org/licenses/gpl-3.0.html
12
 */
13
14
namespace Romm\Formz\Validation\Form;
15
16
use Romm\Formz\Behaviours\BehavioursManager;
17
use Romm\Formz\Condition\Processor\ConditionProcessor;
18
use Romm\Formz\Condition\Processor\ConditionProcessorFactory;
19
use Romm\Formz\Condition\Processor\DataObject\PhpConditionDataObject;
20
use Romm\Formz\Core\Core;
21
use Romm\Formz\Error\FormResult;
22
use Romm\Formz\Form\Definition\Field\Field;
23
use Romm\Formz\Form\Definition\Field\Validation\Validator;
24
use Romm\Formz\Form\FormObject\FormObject;
25
use Romm\Formz\Service\MessageService;
26
use Romm\Formz\Validation\DataObject\ValidatorDataObject;
27
use Romm\Formz\Validation\Validator\AbstractValidator;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Extbase\Error\Result;
30
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
31
use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface;
32
33
class FormValidatorExecutor
34
{
35
    /**
36
     * @var FormObject
37
     */
38
    protected $formObject;
39
40
    /**
41
     * @var ConditionProcessor
42
     */
43
    private $conditionProcessor;
44
45
    /**
46
     * @var array
47
     */
48
    protected $fieldsActivationChecked = [];
49
50
    /**
51
     * Current queue, used to prevent infinite loop.
52
     *
53
     * @var array
54
     */
55
    protected $fieldsActivationChecking = [];
56
57
    /**
58
     * @var array
59
     */
60
    protected $fieldsValidated = [];
61
62
    /**
63
     * Array of arbitral data which are handled by validators.
64
     *
65
     * @var array
66
     */
67
    protected $validationData = [];
68
69
    /**
70
     * @var PhpConditionDataObject
71
     */
72
    protected $phpConditionDataObject;
73
74
    /**
75
     * @param FormObject $formObject
76
     */
77
    public function __construct(FormObject $formObject)
78
    {
79
        $this->formObject = $formObject;
80
        $this->conditionProcessor = $this->getConditionProcessor();
81
        $this->phpConditionDataObject = $this->getPhpConditionDataObject();
82
    }
83
84
    /**
85
     * @return FormValidatorExecutor
86
     */
87
    public function applyBehaviours()
88
    {
89
        /** @var BehavioursManager $behavioursManager */
90
        $behavioursManager = GeneralUtility::makeInstance(BehavioursManager::class);
91
        $behavioursManager->applyBehaviourOnFormInstance($this->formObject);
92
93
        return $this;
94
    }
95
96
    /**
97
     * This function will take care of deactivating the validation for fields
98
     * that do not match their activation condition.
99
     *
100
     * @return FormValidatorExecutor
101
     */
102
    public function checkFieldsActivation()
103
    {
104
        foreach ($this->formObject->getDefinition()->getFields() as $field) {
105
            if (false === $this->getResult()->fieldIsDeactivated($field)) {
106
                $this->checkFieldActivation($field);
107
            }
108
        }
109
110
        return $this;
111
    }
112
113
    /**
114
     * @param Field $field
115
     */
116
    protected function checkFieldActivation(Field $field)
117
    {
118
        // Prevents loop checking.
119
        if ($this->fieldActivationIsBeingChecked($field)
120
            || $this->fieldActivationHasBeenChecked($field)
121
        ) {
122
            return;
123
        }
124
125
        $this->markFieldActivationCheckBegin($field);
126
127
        if (true === $field->hasActivation()
128
            && false === $this->getFieldActivationProcessResult($field)
129
        ) {
130
            $this->getResult()->deactivateField($field);
131
        }
132
133
        $this->checkFieldValidatorActivation($field);
134
135
        $this->markFieldActivationAsChecked($field);
136
        $this->markFieldActivationCheckEnd($field);
137
    }
138
139
    /**
140
     * @param Field $field
141
     */
142
    protected function checkFieldValidatorActivation(Field $field)
143
    {
144
        foreach ($field->getValidators() as $validator) {
145
            if (true === $validator->hasActivation()
146
                && false === $this->getValidatorActivationProcessResult($validator)
147
            ) {
148
                $this->getResult()->deactivateValidator($validator);
149
            }
150
        }
151
    }
152
153
    /**
154
     * @param callable $callback
155
     * @return FormValidatorExecutor
156
     */
157
    public function validateFields(callable $callback = null)
158
    {
159
        foreach ($this->formObject->getDefinition()->getFields() as $field) {
160
            $this->validateField($field);
161
162
            if ($callback
163
                && $this->fieldWasValidated($field)
164
            ) {
165
                call_user_func($callback, $field);
166
            }
167
        }
168
169
        return $this;
170
    }
171
172
    /**
173
     * Will loop on each validator and apply it on the field. The validation
174
     * result is merged with the form result.
175
     *
176
     * @param Field $field
177
     */
178
    public function validateField(Field $field)
179
    {
180
        if (false === $this->fieldWasValidated($field)) {
181
            $this->checkFieldActivation($field);
182
183
            if (false === $this->getResult()->fieldIsDeactivated($field)) {
184
                $this->markFieldAsValidated($field);
185
186
                // Looping on the field's validators settings...
187
                foreach ($field->getValidators() as $validator) {
188
                    if ($this->getResult()->validatorIsDeactivated($validator)) {
189
                        continue;
190
                    }
191
192
                    $validatorResult = $this->processFieldValidator($field, $validator);
193
194
                    // Breaking the loop if an error occurred: we stop the validation process for the current field.
195
                    if ($validatorResult->hasErrors()) {
196
                        break;
197
                    }
198
                }
199
            }
200
        }
201
    }
202
203
    /**
204
     * @param Field     $field
205
     * @param Validator $validator
206
     * @return Result
207
     */
208
    protected function processFieldValidator(Field $field, Validator $validator)
209
    {
210
        $form = $this->formObject->getForm();
211
        $fieldName = $field->getName();
212
        $fieldValue = ObjectAccess::getProperty($form, $fieldName);
213
        $validatorDataObject = new ValidatorDataObject($this->formObject, $validator);
214
215
        /** @var ValidatorInterface $validatorInstance */
216
        $validatorInstance = Core::instantiate(
217
            $validator->getClassName(),
218
            $validator->getOptions(),
219
            $validatorDataObject
220
        );
221
222
        $validatorResult = $validatorInstance->validate($fieldValue);
223
        $validatorResult = MessageService::get()->sanitizeValidatorResult($validatorResult, $validator->getName());
224
225
        if ($validatorInstance instanceof AbstractValidator
226
            && false === empty($validationData = $validatorInstance->getValidationData())
227
        ) {
228
            $this->validationData[$fieldName] = ($this->validationData[$fieldName]) ?: [];
229
            $this->validationData[$fieldName] = array_merge(
230
                $this->validationData[$fieldName],
231
                $validationData
232
            );
233
234
            $form->setValidationData($this->validationData);
0 ignored issues
show
Deprecated Code introduced by
The method Romm\Formz\Form\FormInterface::setValidationData() has been deprecated with message: This method is deprecated and will be deleted in FormZ v2.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
235
        }
236
237
        $this->getResult()->forProperty($fieldName)->merge($validatorResult);
238
        unset($validatorDataObject);
239
240
        return $validatorResult;
241
    }
242
243
    /**
244
     * @return FormResult
245
     */
246
    public function getResult()
247
    {
248
        return $this->formObject->getFormResult();
249
    }
250
251
    /**
252
     * @param Field $field
253
     * @return bool
254
     */
255
    protected function getFieldActivationProcessResult(Field $field)
256
    {
257
        return $this->conditionProcessor->getActivationConditionTreeForField($field)->getPhpResult($this->phpConditionDataObject);
258
    }
259
260
    /**
261
     * @param Validator $validator
262
     * @return bool
263
     */
264
    protected function getValidatorActivationProcessResult(Validator $validator)
265
    {
266
        return $this->conditionProcessor->getActivationConditionTreeForValidator($validator)->getPhpResult($this->phpConditionDataObject);
267
    }
268
269
    /**
270
     * @param Field $field
271
     * @return bool
272
     */
273
    protected function fieldActivationHasBeenChecked(Field $field)
274
    {
275
        return in_array($field->getName(), $this->fieldsActivationChecked);
276
    }
277
278
    /**
279
     * @param Field $field
280
     */
281
    protected function markFieldActivationAsChecked(Field $field)
282
    {
283
        $this->fieldsActivationChecked[] = $field->getName();
284
    }
285
286
    /**
287
     * @param Field $field
288
     * @return bool
289
     */
290
    protected function fieldActivationIsBeingChecked(Field $field)
291
    {
292
        return isset($this->fieldsActivationChecking[$field->getName()]);
293
    }
294
295
    /**
296
     * @param Field $field
297
     */
298
    protected function markFieldActivationCheckBegin(Field $field)
299
    {
300
        $this->fieldsActivationChecking[$field->getName()] = true;
301
    }
302
303
    /**
304
     * @param Field $field
305
     */
306
    protected function markFieldActivationCheckEnd(Field $field)
307
    {
308
        unset($this->fieldsActivationChecking[$field->getName()]);
309
    }
310
311
    /**
312
     * @param Field $field
313
     * @return bool
314
     */
315
    protected function fieldWasValidated(Field $field)
316
    {
317
        return in_array($field->getName(), $this->fieldsValidated);
318
    }
319
320
    /**
321
     * @param Field $field
322
     */
323
    protected function markFieldAsValidated(Field $field)
324
    {
325
        $this->fieldsValidated[] = $field->getName();
326
    }
327
328
    /**
329
     * @return ConditionProcessor
330
     */
331
    protected function getConditionProcessor()
332
    {
333
        return ConditionProcessorFactory::getInstance()->get($this->formObject);
334
    }
335
336
    /**
337
     * @return PhpConditionDataObject
338
     */
339
    protected function getPhpConditionDataObject()
340
    {
341
        return new PhpConditionDataObject($this->formObject->getForm(), $this);
342
    }
343
}
344