Completed
Push — feature/middleware-tmp ( 9fb251...1ed36f )
by Romain
02:05
created

AbstractFormValidator::isDummy()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
252
    }
253
254
    /**
255
     * @param FormInterface $form
256
     * @return FormObjectProxy
257
     */
258
    protected function getProxy(FormInterface $form)
259
    {
260
        return FormObjectFactory::get()->getProxy($form);
0 ignored issues
show
Bug introduced by
It seems like getProxy() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
261
    }
262
263
    /**
264
     * @return FormValidatorDataObject
265
     */
266
    public function getDataObject()
267
    {
268
        if (null === $this->dataObject) {
269
            $this->dataObject = Core::instantiate(FormValidatorDataObject::class);
270
        }
271
272
        return $this->dataObject;
273
    }
274
}
275