Completed
Pull Request — wip/steps (#71)
by
unknown
02:46
created

FormViewHelperService   B

Complexity

Total Complexity 38

Size/Duplication

Total Lines 313
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 38
lcom 1
cbo 12
dl 0
loc 313
rs 8.3999
c 1
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A resetState() 0 6 1
A activateFormContext() 0 11 2
A formContextExists() 0 4 1
B applyBehavioursOnSubmittedForm() 0 24 4
B injectFormRequestData() 0 23 4
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\Form;
15
16
use DateTime;
17
use Romm\Formz\AssetHandler\Html\DataAttributesAssetHandler;
18
use Romm\Formz\Behaviours\BehavioursManager;
19
use Romm\Formz\Core\Core;
20
use Romm\Formz\Error\FormResult;
21
use Romm\Formz\Exceptions\DuplicateEntryException;
22
use Romm\Formz\Form\Definition\Step\Step\Step;
23
use Romm\Formz\Form\FormObject\FormObject;
24
use Romm\Formz\Validation\Validator\Form\AbstractFormValidator;
25
use Romm\Formz\Validation\Validator\Form\DefaultFormValidator;
26
use Traversable;
27
use TYPO3\CMS\Core\SingletonInterface;
28
use TYPO3\CMS\Core\Utility\GeneralUtility;
29
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
30
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
31
use TYPO3\CMS\Extbase\Error\Error;
32
use TYPO3\CMS\Extbase\Error\Result;
33
use TYPO3\CMS\Extbase\Mvc\Web\Request;
34
use TYPO3\CMS\Fluid\Core\ViewHelper\ViewHelperVariableContainer;
35
use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper;
36
37
/**
38
 * This class contains methods that help view helpers to manipulate data and
39
 * know more things concerning the current form state.
40
 *
41
 * It is mainly configured inside the `FormViewHelper`, and used in other
42
 * view helpers.
43
 */
44
class FormViewHelperService implements SingletonInterface
45
{
46
    /**
47
     * @var bool
48
     */
49
    protected $formContext = false;
50
51
    /**
52
     * @var FormObject
53
     */
54
    protected $formObject;
55
56
    /**
57
     * @var Request
58
     */
59
    protected $request;
60
61
    /**
62
     * @var Result
63
     */
64
    protected $result;
65
66
    /**
67
     * Reset every state that can be used by this service.
68
     */
69
    public function resetState()
70
    {
71
        $this->formContext = false;
72
        $this->formObject = null;
73
        $this->request = null;
74
    }
75
76
    /**
77
     * Will activate the form context, changing the result returned by the
78
     * function `formContextExists()`.
79
     *
80
     * @return FormViewHelperService
81
     * @throws DuplicateEntryException
82
     */
83
    public function activateFormContext()
84
    {
85
        if (true === $this->formContext) {
86
            throw DuplicateEntryException::duplicatedFormContext();
87
        }
88
89
        $this->formContext = true;
90
        $this->result = new Result;
91
92
        return $this;
93
    }
94
95
    /**
96
     * Returns `true` if the `FormViewHelper` context exists.
97
     *
98
     * @return bool
99
     */
100
    public function formContextExists()
101
    {
102
        return $this->formContext;
103
    }
104
105
    /**
106
     * Will loop on the submitted form fields and apply behaviours if their
107
     * configuration contains.
108
     */
109
    public function applyBehavioursOnSubmittedForm()
110
    {
111
        if ($this->formObject->formWasSubmitted()) {
112
            $request = $this->request->getOriginalRequest();
113
            $formName = $this->formObject->getName();
114
115
            if ($request
116
                && $request->hasArgument($formName)
117
            ) {
118
                /** @var BehavioursManager $behavioursManager */
119
                $behavioursManager = GeneralUtility::makeInstance(BehavioursManager::class);
120
121
                /** @var array $originalForm */
122
                $originalForm = $request->getArgument($formName);
123
124
                $formProperties = $behavioursManager->applyBehaviourOnPropertiesArray(
125
                    $originalForm,
126
                    $this->formObject->getDefinition()
127
                );
128
129
                $request->setArgument($formName, $formProperties);
130
            }
131
        }
132
    }
133
134
    /**
135
     * Takes care of injecting data for the form.
136
     *
137
     * If the form was generated using a content object, information about it
138
     * are injected, to be retrieved later to be able for instance to fetch the
139
     * object settings (TypoScript, FlexForm, ...).
140
     */
141
    public function injectFormRequestData()
142
    {
143
        if (false === $this->formObject->hasForm()) {
144
            return;
145
        }
146
147
        $requestData = $this->formObject->getRequestData();
148
149
        $currentStepIdentifier = $this->getCurrentStep()
150
            ? $this->getCurrentStep()->getIdentifier()
151
            : null;
152
        $requestData->setCurrentStepIdentifier($currentStepIdentifier);
153
154
        /** @var ConfigurationManager $configurationManager */
155
        $configurationManager = Core::instantiate(ConfigurationManagerInterface::class);
156
157
        $contentObject = $configurationManager->getContentObject();
158
159
        if (null !== $contentObject) {
160
            $requestData->setContentObjectTable($contentObject->getCurrentTable());
161
            $requestData->setContentObjectUid($contentObject->data['uid']);
162
        }
163
    }
164
165
    /**
166
     * Fetches all data attributes that are bound to the form: fields values,
167
     * validation result and others.
168
     *
169
     * @param DataAttributesAssetHandler $dataAttributesAssetHandler
170
     * @return array
171
     */
172
    public function getDataAttributes(DataAttributesAssetHandler $dataAttributesAssetHandler)
173
    {
174
        $dataAttributes = [];
175
176
        if ($this->formObject->hasForm()) {
177
            /*
178
             * Getting the data attributes for the form values. It is needed to
179
             * have a validation result because a field can be deactivated (in
180
             * that case, the data attribute for this field is removed).
181
             */
182
            if (false === $this->formObject->formWasValidated()) {
183
                $formResult = $this->getFormValidationResult();
184
            } else {
185
                $formResult = $this->formObject->getFormResult();
186
            }
187
188
            $dataAttributes += $dataAttributesAssetHandler->getFieldsValuesDataAttributes($formResult);
189
        }
190
191
        if (true === $this->formObject->formWasSubmitted()) {
192
            $dataAttributes += $dataAttributesAssetHandler->getFieldSubmissionDoneDataAttribute();
193
        }
194
195
        if (true === $this->formObject->formWasValidated()) {
196
            $dataAttributes += $dataAttributesAssetHandler->getFieldsValidDataAttributes();
197
            $dataAttributes += $dataAttributesAssetHandler->getFieldsMessagesDataAttributes();
198
        }
199
200
        return $dataAttributes;
201
    }
202
203
    /**
204
     * Checks if the form uses steps, in which case the current step is needed
205
     * in order to display the form. If the step is not found, an exception is
206
     * thrown.
207
     */
208
    public function checkStepDefinition()
209
    {
210
        if ($this->formObject->getDefinition()->hasSteps()
211
            && null === $this->getCurrentStep()
212
        ) {
213
            throw new \Exception('todo'); // @todo
214
        }
215
    }
216
217
    /**
218
     * Will check all the fields that have been added below the form view
219
     * helper: each field that is found in the form definition and is *not*
220
     * supported by the current step will add an error and cancel the form
221
     * rendering.
222
     *
223
     * @param ViewHelperVariableContainer $variableContainer
224
     */
225
    public function checkStepFields(ViewHelperVariableContainer $variableContainer)
226
    {
227
        $currentStep = $this->getCurrentStep();
228
229
        if (null === $currentStep) {
230
            return;
231
        }
232
233
        $unsupportedFieldsList = [];
234
        $formDefinition = $this->formObject->getDefinition();
235
        $fieldNames = $this->getCurrentFormFieldNames($variableContainer);
236
237
        foreach ($fieldNames as $fieldName) {
238
            if (false === $formDefinition->hasField($fieldName)) {
239
                continue;
240
            }
241
242
            $field = $formDefinition->getField($fieldName);
243
244
            if (false === $currentStep->supportsField($field)) {
245
                $unsupportedFieldsList[] = $fieldName;
246
            }
247
        }
248
249
        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...
250
            $error = new Error(
251
                '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.',
252
                1494430935,
253
                [$currentStep->getIdentifier(), implode('", "', $unsupportedFieldsList)]
254
            );
255
            $this->result->addError($error);
256
        }
257
    }
258
259
    /**
260
     * Returns the list of fields that have been added below the form view
261
     * helper.
262
     *
263
     * @param ViewHelperVariableContainer $variableContainer
264
     * @return array
265
     */
266
    public function getCurrentFormFieldNames(ViewHelperVariableContainer $variableContainer)
267
    {
268
        $formFieldNames = $variableContainer->get(FormViewHelper::class, 'formFieldNames');
269
        $cleanFormFieldNames = [];
270
271
        foreach ($formFieldNames as $fieldName) {
272
            $explode = explode('[', $fieldName);
273
274
            if (count($explode) >= 3) {
275
                $formName = rtrim($explode[1], ']');
276
                $fieldName = rtrim($explode[2], ']');
277
278
                if ($formName === $this->formObject->getName()
279
                    && $fieldName !== '__identity'
280
                ) {
281
                    $cleanFormFieldNames[$fieldName] = $fieldName;
282
                }
283
            }
284
        }
285
286
        return $cleanFormFieldNames;
287
    }
288
289
    /**
290
     * @return Step|null
291
     */
292
    public function getCurrentStep()
293
    {
294
        return $this->formObject->fetchCurrentStep($this->request)->getCurrentStep();
295
    }
296
297
    /**
298
     * @return FormObject
299
     */
300
    public function getFormObject()
301
    {
302
        return $this->formObject;
303
    }
304
305
    /**
306
     * @param FormObject $formObject
307
     */
308
    public function setFormObject(FormObject $formObject)
309
    {
310
        $this->formObject = $formObject;
311
    }
312
313
    /**
314
     * @param Request $request
315
     */
316
    public function setRequest(Request $request)
317
    {
318
        $this->request = $request;
319
    }
320
321
    /**
322
     * @return Result
323
     */
324
    public function getResult()
325
    {
326
        return $this->result;
327
    }
328
329
    /**
330
     * @return FormResult
331
     */
332
    protected function getFormValidationResult()
333
    {
334
        $formValidator = $this->getFormValidator($this->formObject->getName());
335
336
        return $formValidator->validate($this->formObject->getForm());
337
    }
338
339
    /**
340
     * @param string $formName
341
     * @return AbstractFormValidator
342
     */
343
    protected function getFormValidator($formName)
344
    {
345
        /** @var AbstractFormValidator $validator */
346
        $validator = Core::instantiate(
347
            DefaultFormValidator::class,
348
            [
349
                'name'  => $formName,
350
                'dummy' => true
351
            ]
352
        );
353
354
        return $validator;
355
    }
356
}
357