Completed
Push — feature/middleware ( 864c18 )
by Romain
03:21
created

FormViewHelperService   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 308
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 0
Metric Value
wmc 37
lcom 1
cbo 12
dl 0
loc 308
rs 8.6
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A formContextExists() 0 4 1
A resetState() 0 6 1
A activateFormContext() 0 11 2
B applyBehavioursOnSubmittedForm() 0 24 4
A injectFormRequestData() 0 18 3
B getDataAttributes() 0 30 5
A checkStepDefinition() 0 8 3
B checkStepFields() 0 33 6
B getCurrentFormFieldNames() 0 22 5
A getCurrentStep() 0 4 1
A getFormObject() 0 4 1
A setFormObject() 0 4 1
A setRequest() 0 4 1
A getResult() 0 4 1
A getFormValidationResult() 0 6 1
A getFormValidator() 0 13 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\Service\ViewHelper;
15
16
use Romm\Formz\AssetHandler\Html\DataAttributesAssetHandler;
17
use Romm\Formz\Behaviours\BehavioursManager;
18
use Romm\Formz\Core\Core;
19
use Romm\Formz\Error\FormResult;
20
use Romm\Formz\Exceptions\DuplicateEntryException;
21
use Romm\Formz\Form\Definition\Step\Step\Step;
22
use Romm\Formz\Form\FormObject\FormObject;
23
use Romm\Formz\Validation\Validator\Form\AbstractFormValidator;
24
use Romm\Formz\Validation\Validator\Form\DefaultFormValidator;
25
use TYPO3\CMS\Core\SingletonInterface;
26
use TYPO3\CMS\Core\Utility\GeneralUtility;
27
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
28
use TYPO3\CMS\Extbase\Error\Error;
29
use TYPO3\CMS\Extbase\Error\Result;
30
use TYPO3\CMS\Extbase\Mvc\Web\Request;
31
use TYPO3\CMS\Fluid\Core\ViewHelper\ViewHelperVariableContainer;
32
use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper;
33
34
/**
35
 * This class contains methods that help view helpers to manipulate data and
36
 * know more things concerning the current form state.
37
 *
38
 * It is mainly configured inside the `FormViewHelper`, and used in other
39
 * view helpers.
40
 */
41
class FormViewHelperService implements SingletonInterface
42
{
43
    /**
44
     * @var bool
45
     */
46
    protected $formContext = false;
47
48
    /**
49
     * @var FormObject
50
     */
51
    protected $formObject;
52
53
    /**
54
     * @var Request
55
     */
56
    protected $request;
57
58
    /**
59
     * @var Result
60
     */
61
    protected $result;
62
63
    /**
64
     * Reset every state that can be used by this service.
65
     */
66
    public function resetState()
67
    {
68
        $this->formContext = false;
69
        $this->formObject = null;
70
        $this->request = null;
71
    }
72
73
    /**
74
     * Will activate the form context, changing the result returned by the
75
     * function `formContextExists()`.
76
     *
77
     * @return FormViewHelperService
78
     * @throws DuplicateEntryException
79
     */
80
    public function activateFormContext()
81
    {
82
        if (true === $this->formContext) {
83
            throw DuplicateEntryException::duplicatedFormContext();
84
        }
85
86
        $this->formContext = true;
87
        $this->result = new Result;
88
89
        return $this;
90
    }
91
92
    /**
93
     * Returns `true` if the `FormViewHelper` context exists.
94
     *
95
     * @return bool
96
     */
97
    public function formContextExists()
98
    {
99
        return $this->formContext;
100
    }
101
102
    /**
103
     * Will loop on the submitted form fields and apply behaviours if their
104
     * configuration contains.
105
     */
106
    public function applyBehavioursOnSubmittedForm()
107
    {
108
        if ($this->formObject->formWasSubmitted()) {
109
            $request = $this->request->getOriginalRequest();
110
            $formName = $this->formObject->getName();
111
112
            if ($request
113
                && $request->hasArgument($formName)
114
            ) {
115
                /** @var BehavioursManager $behavioursManager */
116
                $behavioursManager = GeneralUtility::makeInstance(BehavioursManager::class);
117
118
                /** @var array $originalForm */
119
                $originalForm = $request->getArgument($formName);
120
121
                $formProperties = $behavioursManager->applyBehaviourOnPropertiesArray(
122
                    $originalForm,
123
                    $this->formObject->getDefinition()
124
                );
125
126
                $request->setArgument($formName, $formProperties);
127
            }
128
        }
129
    }
130
131
    /**
132
     * Takes care of injecting data for the form.
133
     *
134
     * If the form was generated using a content object, information about it
135
     * are injected, to be retrieved later to be able for instance to fetch the
136
     * object settings (TypoScript, FlexForm, ...).
137
     */
138
    public function injectFormRequestData()
139
    {
140
        if (false === $this->formObject->hasForm()) {
141
            return;
142
        }
143
144
        /** @var ConfigurationManager $configurationManager */
145
        $configurationManager = Core::instantiate(ConfigurationManager::class);
146
147
        $contentObject = $configurationManager->getContentObject();
148
149
        if (null !== $contentObject) {
150
            $requestData = $this->formObject->getRequestData();
151
152
            $requestData->setContentObjectTable($contentObject->getCurrentTable());
153
            $requestData->setContentObjectUid($contentObject->data['uid']);
154
        }
155
    }
156
157
    /**
158
     * Fetches all data attributes that are bound to the form: fields values,
159
     * validation result and others.
160
     *
161
     * @param DataAttributesAssetHandler $dataAttributesAssetHandler
162
     * @return array
163
     */
164
    public function getDataAttributes(DataAttributesAssetHandler $dataAttributesAssetHandler)
165
    {
166
        $dataAttributes = [];
167
168
        if ($this->formObject->hasForm()) {
169
            /*
170
             * Getting the data attributes for the form values. It is needed to
171
             * have a validation result because a field can be deactivated (in
172
             * that case, the data attribute for this field is removed).
173
             */
174
            if (false === $this->formObject->formWasValidated()) {
175
                $formResult = $this->getFormValidationResult();
176
            } else {
177
                $formResult = $this->formObject->getFormResult();
178
            }
179
180
            $dataAttributes += $dataAttributesAssetHandler->getFieldsValuesDataAttributes($formResult);
181
        }
182
183
        if (true === $this->formObject->formWasSubmitted()) {
184
            $dataAttributes += $dataAttributesAssetHandler->getFieldSubmissionDoneDataAttribute();
185
        }
186
187
        if (true === $this->formObject->formWasValidated()) {
188
            $dataAttributes += $dataAttributesAssetHandler->getFieldsValidDataAttributes();
189
            $dataAttributes += $dataAttributesAssetHandler->getFieldsMessagesDataAttributes();
190
        }
191
192
        return $dataAttributes;
193
    }
194
195
    /**
196
     * Checks if the form uses steps, in which case the current step is needed
197
     * in order to display the form. If the step is not found, an exception is
198
     * thrown.
199
     */
200
    public function checkStepDefinition()
201
    {
202
        if ($this->formObject->getDefinition()->hasSteps()
203
            && null === $this->getCurrentStep()
204
        ) {
205
            throw new \Exception('todo'); // @todo
206
        }
207
    }
208
209
    /**
210
     * Will check all the fields that have been added below the form view
211
     * helper: each field that is found in the form definition and is *not*
212
     * supported by the current step will add an error and cancel the form
213
     * rendering.
214
     *
215
     * @param ViewHelperVariableContainer $variableContainer
216
     */
217
    public function checkStepFields(ViewHelperVariableContainer $variableContainer)
218
    {
219
        $currentStep = $this->getCurrentStep();
220
221
        if (null === $currentStep) {
222
            return;
223
        }
224
225
        $unsupportedFieldsList = [];
226
        $formDefinition = $this->formObject->getDefinition();
227
        $fieldNames = $this->getCurrentFormFieldNames($variableContainer);
228
229
        foreach ($fieldNames as $fieldName) {
230
            if (false === $formDefinition->hasField($fieldName)) {
231
                continue;
232
            }
233
234
            $field = $formDefinition->getField($fieldName);
235
236
            if (false === $currentStep->supportsField($field)) {
237
                $unsupportedFieldsList[] = $fieldName;
238
            }
239
        }
240
241
        if ($unsupportedFieldsList) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $unsupportedFieldsList of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
242
            $error = new Error(
243
                'The following fields are not supported by the step "%s": "%s". Please add these fields to the supported fields list of the step in order to render it in your template.',
244
                1494430935,
245
                [$currentStep->getIdentifier(), implode('", "', $unsupportedFieldsList)]
246
            );
247
            $this->result->addError($error);
248
        }
249
    }
250
251
    /**
252
     * Returns the list of fields that have been added below the form view
253
     * helper.
254
     *
255
     * @param ViewHelperVariableContainer $variableContainer
256
     * @return array
257
     */
258
    public function getCurrentFormFieldNames(ViewHelperVariableContainer $variableContainer)
259
    {
260
        $formFieldNames = $variableContainer->get(FormViewHelper::class, 'formFieldNames');
261
        $cleanFormFieldNames = [];
262
263
        foreach ($formFieldNames as $fieldName) {
264
            $explode = explode('[', $fieldName);
265
266
            if (count($explode) >= 3) {
267
                $formName = rtrim($explode[1], ']');
268
                $fieldName = rtrim($explode[2], ']');
269
270
                if ($formName === $this->formObject->getName()
271
                    && $fieldName !== '__identity'
272
                ) {
273
                    $cleanFormFieldNames[$fieldName] = $fieldName;
274
                }
275
            }
276
        }
277
278
        return $cleanFormFieldNames;
279
    }
280
281
    /**
282
     * @return Step|null
283
     */
284
    public function getCurrentStep()
285
    {
286
        return $this->formObject->fetchCurrentStep($this->request)->getCurrentStep();
287
    }
288
289
    /**
290
     * @return FormObject
291
     */
292
    public function getFormObject()
293
    {
294
        return $this->formObject;
295
    }
296
297
    /**
298
     * @param FormObject $formObject
299
     */
300
    public function setFormObject(FormObject $formObject)
301
    {
302
        $this->formObject = $formObject;
303
    }
304
305
    /**
306
     * @param Request $request
307
     */
308
    public function setRequest(Request $request)
309
    {
310
        $this->request = $request;
311
    }
312
313
    /**
314
     * @return Result
315
     */
316
    public function getResult()
317
    {
318
        return $this->result;
319
    }
320
321
    /**
322
     * @return FormResult
323
     */
324
    protected function getFormValidationResult()
325
    {
326
        $formValidator = $this->getFormValidator($this->formObject->getName());
327
328
        return $formValidator->validate($this->formObject->getForm());
329
    }
330
331
    /**
332
     * @param string $formName
333
     * @return AbstractFormValidator
334
     */
335
    protected function getFormValidator($formName)
336
    {
337
        /** @var AbstractFormValidator $validator */
338
        $validator = Core::instantiate(
339
            DefaultFormValidator::class,
340
            [
341
                'name'  => $formName,
342
                'dummy' => true
343
            ]
344
        );
345
346
        return $validator;
347
    }
348
}
349