Completed
Push — wip/steps ( 47ee47 )
by Romain
03:13
created

FormViewHelperService::setFormObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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