Completed
Push — middleware-wip ( ab9573...668c38 )
by Romain
10:14
created

AbstractFormValidator::setResult()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
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\Validator\Form;
15
16
use Romm\Formz\Configuration\Form\Field\Field;
17
use Romm\Formz\Core\Core;
18
use Romm\Formz\Error\FormResult;
19
use Romm\Formz\Exceptions\InvalidArgumentTypeException;
20
use Romm\Formz\Form\FormInterface;
21
use Romm\Formz\Form\FormObject\FormObject;
22
use Romm\Formz\Form\FormObject\FormObjectFactory;
23
use Romm\Formz\Service\FormService;
24
use Romm\Formz\Validation\Validator\Form\DataObject\FormValidatorDataObject;
25
use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator as ExtbaseAbstractValidator;
26
27
/**
28
 * This is the abstract form validator, which must be inherited by any custom
29
 * form validator in order to work properly.
30
 *
31
 * Please note that a default form validator already exists if you need a form
32
 * which does not require any particular action: `DefaultFormValidator`.
33
 *
34
 * A form validator should be called to validate any form instance (which is a
35
 * child of `AbstractForm`). Usually, this is used in controller actions to
36
 * validate a form sent by the user. Example:
37
 *
38
 * /**
39
 *  * Action called when the Example form is submitted.
40
 *  *
41
 *  * @param $exForm
42
 *  * @validate $exForm Romm.Formz:Form\DefaultFormValidator
43
 *  * /
44
 *  public function submitFormAction(ExampleForm $exForm) { ... }
45
 *
46
 *******************************************************************************
47
 *
48
 * You may use you own custom form validator in order to be able to use the
49
 * following features:
50
 *
51
 * - Pre-validation custom process:
52
 *   By extending the method `beforeValidationProcess()`, you are able to handle
53
 *   anything you want just before the form validation begins to loop on every
54
 *   field. This can be used for instance to (de)activate the validation of
55
 *   certain fields under very specific circumstances.
56
 *
57
 * - In real time custom process:
58
 *   After each field went trough a validation process, a magic method is called
59
 *   to allow very low level custom process. The magic method name looks like:
60
 *   "{lowerCamelCaseFieldName}Validated". For instance, when the "email" field
61
 *   just went trough the validation process, the method `emailValidated()` is
62
 *   called.
63
 *
64
 * - Post-validation custom process:
65
 *   After the validation was done on every field of the form, this method is
66
 *   called to allow you high level process. For instance, let's assume your
67
 *   form is used to calculate a price estimation depending on information
68
 *   submitted in the form; when the form went trough the validation process and
69
 *   got no error, you can run the price estimation, and if any error occurs you
70
 *   are still able to add an error to `$this->result` (in a controller you do
71
 *   not have access to it anymore).
72
 */
73
abstract class AbstractFormValidator extends ExtbaseAbstractValidator implements FormValidatorInterface
74
{
75
    /**
76
     * @inheritdoc
77
     */
78
    protected $supportedOptions = [
79
        'name' => ['', 'Name of the form.', 'string', true]
80
    ];
81
82
    /**
83
     * @var FormResult
84
     */
85
    protected $result;
86
87
    /**
88
     * @var FormResult
89
     */
90
    protected $initialResult;
91
92
    /**
93
     * @var FormInterface
94
     */
95
    protected $form;
96
97
    /**
98
     * @var FormObject
99
     */
100
    protected $formObject;
101
102
    /**
103
     * @var FormValidatorExecutor
104
     */
105
    private $formValidatorExecutor;
106
107
    /**
108
     * @var FormValidatorDataObject
109
     */
110
    protected $dataObject;
111
112
    /**
113
     * Initializes all class variables.
114
     *
115
     * @param FormInterface $form
116
     * @throws InvalidArgumentTypeException
117
     */
118
    private function initializeValidator($form)
119
    {
120
        if (false === $form instanceof FormInterface) {
121
            throw InvalidArgumentTypeException::validatingWrongFormType(get_class($form));
122
        }
123
124
        $this->form = $form;
125
        $this->formObject = FormObjectFactory::get()->getInstanceFromFormInstance($this->form, $this->options['name']);
126
        $this->formValidatorExecutor = $this->getFormValidatorExecutor();
127
128
        $this->getDataObject()->addFieldValidationCallback(function (Field $field) {
129
            $this->afterFieldValidation($field);
130
        });
131
132
        if (null === $this->initialResult) {
133
            $this->initialResult = $this->getResult();
134
        }
135
    }
136
137
    /**
138
     * Checks the given form instance, and launches the validation if it is a
139
     * correct form.
140
     *
141
     * @param FormInterface $form The form instance to be validated.
142
     * @return FormResult
143
     */
144
    final public function validate($form)
145
    {
146
        $this->initializeValidator($form);
147
148
        $proxy = FormObjectFactory::get()->getProxy($this->formObject, $form);
149
        $proxy->markFormAsSubmitted();
150
151
        $this->validateGhost($form, false);
152
153
        if ($this->initialResult !== $this->result) {
154
            $this->initialResult->merge($this->result);
155
        }
156
157
        $proxy->setFormResult($this->initialResult);
158
159
        return $this->initialResult;
160
    }
161
162
    /**
163
     * Validates the form, but wont save form data in the form object.
164
     *
165
     * @param FormInterface $form
166
     * @param bool          $initialize
167
     * @return FormResult
168
     * @internal
169
     */
170
    public function validateGhost($form, $initialize = true)
171
    {
172
        if ($initialize) {
173
            $this->initializeValidator($form);
174
        }
175
176
        $this->isValid($form);
177
178
        return $this->result;
179
    }
180
181
    /**
182
     * Runs the whole validation workflow.
183
     *
184
     * @param FormInterface $form
185
     */
186
    final public function isValid($form)
187
    {
188
        $this->formValidatorExecutor->applyBehaviours();
189
        $this->formValidatorExecutor->checkFieldsActivation();
190
191
        $this->beforeValidationProcess();
192
193
        $this->formValidatorExecutor->validateFields();
194
195
        $this->afterValidationProcess();
196
197
        if ($this->result->hasErrors()) {
198
            // Storing the form for possible third party further usage.
199
            FormService::addFormWithErrors($form);
200
        }
201
    }
202
203
    /**
204
     * Override this function in your child class to handle some pre-validation
205
     * process.
206
     */
207
    protected function beforeValidationProcess()
208
    {
209
    }
210
211
    /**
212
     * Override this function in your child class to handle some post-validation
213
     * process.
214
     */
215
    protected function afterValidationProcess()
216
    {
217
    }
218
219
    /**
220
     * After each field has been validated, a matching method can be called if
221
     * it exists in the child class.
222
     *
223
     * The syntax is `{lowerCamelCaseFieldName}Validated()`.
224
     *
225
     * Example: for field `firstName` - `firstNameValidated()`.
226
     *
227
     * @param Field $field
228
     */
229
    protected function afterFieldValidation(Field $field)
230
    {
231
        $functionName = lcfirst($field->getName() . 'Validated');
232
233
        if (method_exists($this, $functionName)) {
234
            call_user_func([$this, $functionName]);
235
        }
236
    }
237
238
    /**
239
     * @return FormValidatorExecutor
240
     */
241
    protected function getFormValidatorExecutor()
242
    {
243
        /** @var FormValidatorExecutor $formValidatorExecutor */
244
        $formValidatorExecutor = Core::instantiate(FormValidatorExecutor::class, $this->formObject, $this->getDataObject());
245
246
        return $formValidatorExecutor;
247
    }
248
249
    /**
250
     * @param FormResult $result
251
     */
252
    public function setInitialResult(FormResult $result)
253
    {
254
        $this->initialResult = $result;
255
    }
256
257
    /**
258
     * @return FormValidatorDataObject
259
     */
260
    public function getDataObject()
261
    {
262
        if (null === $this->dataObject) {
263
            $this->dataObject = Core::instantiate(
264
                FormValidatorDataObject::class,
265
                $this->getResult()
266
            );
267
        }
268
269
        return $this->dataObject;
270
    }
271
272
    /**
273
     * @return FormResult
274
     */
275
    private function getResult()
276
    {
277
        if (null === $this->result) {
278
            $this->result = new FormResult;
279
        }
280
281
        return $this->result;
282
    }
283
}
284