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

StepMiddlewareService::getFirstInvalidStep()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 54
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 54
rs 7.8331
c 0
b 0
f 0
cc 7
eloc 25
nc 8
nop 1

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Middleware\Item\Step\Service;
15
16
use Romm\Formz\Core\Core;
17
use Romm\Formz\Error\FormResult;
18
use Romm\Formz\Form\Definition\Step\Step\Step;
19
use Romm\Formz\Form\Definition\Step\Step\StepDefinition;
20
use Romm\Formz\Form\FormObject\FormObject;
21
use Romm\Formz\Form\FormObject\FormObjectFactory;
22
use Romm\Formz\Form\FormObject\Service\Step\FormStepPersistence;
23
use Romm\Formz\Middleware\Item\FormValidation\FormValidationMiddlewareOption;
24
use Romm\Formz\Middleware\Request\Redirect;
25
use Romm\Formz\Service\Traits\SelfInstantiateTrait;
26
use Romm\Formz\Validation\Validator\Form\AbstractFormValidator;
27
use TYPO3\CMS\Core\SingletonInterface;
28
use TYPO3\CMS\Extbase\Property\PropertyMapper;
29
30
/**
31
 * This service allows extended form steps manipulation.
32
 */
33
class StepMiddlewareService implements SingletonInterface
34
{
35
    use SelfInstantiateTrait;
36
37
    /**
38
     * @var FormObject
39
     */
40
    protected $formObject;
41
42
    /**
43
     * @var FormStepPersistence
44
     */
45
    protected $formStepPersistence;
46
47
    /**
48
     * @param FormObject $formObject
49
     */
50
    public function reset(FormObject $formObject)
51
    {
52
        $this->formObject = $formObject;
53
    }
54
55
    /**
56
     * Marks the given step as validated: no errors were found during validation
57
     * with the given values array.
58
     *
59
     * @param Step  $step
60
     * @param array $formValues
61
     */
62
    public function markStepAsValidated(Step $step, array $formValues)
63
    {
64
        $this->getStepPersistence()->markStepAsValidated($step);
65
        $this->getStepPersistence()->addStepFormValues($step, $formValues);
66
    }
67
68
    /**
69
     * @param array $validatedFields
70
     */
71
    public function addValidatedFields(array $validatedFields)
72
    {
73
        $this->getStepPersistence()->addValidatedFields($validatedFields);
74
    }
75
76
    /**
77
     * Checks that the previous step has already been validated, meaning the
78
     * user has the right to stand in the given step.
79
     *
80
     * @param Step $step
81
     * @return bool
82
     */
83
    public function stepIsValid(Step $step)
84
    {
85
        $stepDefinition = $this->getStepDefinition($step);
86
87
        if (false === $stepDefinition->hasPreviousDefinition()) {
88
            /*
89
             * No previous step definition found: the user stands on the first
90
             * step, it always has the right to stand there.
91
             */
92
            return true;
93
        }
94
95
        $previousStep = $stepDefinition->getPreviousDefinition()->getStep();
96
97
        return $this->getStepPersistence()->stepWasValidated($previousStep);
98
    }
99
100
    /**
101
     * Searches for the first invalid step among previous steps from the given
102
     * step.
103
     *
104
     * All previous steps are listed, then for each one we check if submitted
105
     * form values has been saved in the step persistence, in which case the
106
     * step validation is launched again with the current form configuration.
107
     *
108
     * @param Step $step
109
     * @return Step|null
110
     */
111
    public function getFirstInvalidStep(Step $step)
112
    {
113
        $firstStep = $this->getFirstStepDefinition()->getStep();
114
115
        if ($step === $firstStep) {
116
            /*
117
             * The first step is always valid.
118
             */
119
            return null;
120
        }
121
122
        /*
123
         * If there is no form instance, and the request is not in the first
124
         * step, obviously the user should not be there.
125
         */
126
        if (false === $this->formObject->hasForm()) {
127
            return $firstStep;
128
        }
129
130
        /** @var StepDefinition[] $stepDefinitionsToTest */
131
        $stepDefinitionsToTest = [];
132
        $invalidStep = null;
133
        $stepDefinition = $this->getStepDefinition($step);
134
135
        while ($stepDefinition->hasPreviousDefinition()) {
136
            $stepDefinition = $stepDefinition->getPreviousDefinition();
137
            array_unshift($stepDefinitionsToTest, $stepDefinition);
138
        }
139
140
        foreach ($stepDefinitionsToTest as $stepDefinition) {
141
            $step = $stepDefinition->getStep();
142
143
            /*
144
             * If the already submitted form values are not found, the step is
145
             * considered as invalid.
146
             */
147
            if (false === $this->getStepPersistence()->hasStepFormValues($step)) {
148
                $invalidStep = $step;
149
                break;
150
            }
151
152
            $result = $this->validateStep($step);
153
154
            if ($result->hasErrors()) {
155
                $invalidStep = $step;
156
                break;
157
            } else {
158
                $this->getStepPersistence()->markStepAsValidated($step);
159
                $this->getStepPersistence()->addValidatedFields($result->getValidatedFields());
160
            }
161
        }
162
163
        return $invalidStep;
164
    }
165
166
    /**
167
     * Redirects the current request to the given step.
168
     *
169
     * @param Step     $step
170
     * @param Redirect $redirect
171
     */
172
    public function redirectToStep(Step $step, Redirect $redirect)
173
    {
174
        $redirect->toPage($step->getPageUid())
175
            ->toExtension($step->getExtension())
176
            ->toController($step->getController())
177
            ->toAction($step->getAction())
178
            ->withArguments([
179
                'fz-hash' => [
180
                    $this->formObject->getName() => $this->formObject->getFormHash()
181
                ]
182
            ])
183
            ->dispatch();
184
    }
185
186
    /**
187
     * @param Step $step
188
     * @return StepDefinition|null
189
     */
190
    public function getStepDefinition(Step $step)
191
    {
192
        $stepDefinition = $this->getFirstStepDefinition();
193
194
        while ($stepDefinition) {
195
            if ($stepDefinition->getStep() === $step) {
196
                return $stepDefinition;
197
            }
198
199
            $stepDefinition = $stepDefinition->hasNextSteps()
200
                ? $stepDefinition->getNextSteps()->getDefaultStep()
201
                : null;
202
        }
203
204
        return null;
205
    }
206
207
    /**
208
     * Validates (again) the given step with the form data that were previously
209
     * submitted and fetched from the step persistence.
210
     *
211
     * @param Step $step
212
     * @return FormResult
213
     */
214
    protected function validateStep(Step $step)
215
    {
216
        /** @var FormValidationMiddlewareOption $formValidationMiddlewareOptions */
217
        $formValidationMiddlewareOptions = $this->formObject
218
            ->getDefinition()
219
            ->getPresetMiddlewares()
220
            ->getFormValidationMiddleware()
221
            ->getOptions();
222
223
        /** @var AbstractFormValidator $validator */
224
        $validator = Core::instantiate(
225
            $formValidationMiddlewareOptions->getFormValidatorClassName(),
226
            [
227
                'name'  => $this->formObject->getName(),
228
                'dummy' => true
229
            ]
230
        );
231
232
        $validator->getDataObject()->setValidatedStep($step);
233
234
        /** @var PropertyMapper $propertyMapper */
235
        $propertyMapper = Core::instantiate(PropertyMapper::class);
236
        $form = $propertyMapper->convert($this->getStepPersistence()->getStepFormValues($step), $this->formObject->getClassName());
237
238
        return $validator->validate($form);
239
    }
240
241
    /**
242
     * @return FormStepPersistence
243
     */
244
    protected function getStepPersistence()
245
    {
246
        if (null === $this->formStepPersistence) {
247
            $proxy = FormObjectFactory::get()->getProxy($this->formObject->getForm());
248
249
            $this->formStepPersistence = $proxy->getStepPersistence();
250
        }
251
252
        return $this->formStepPersistence;
253
    }
254
255
    /**
256
     * @return StepDefinition
257
     */
258
    protected function getFirstStepDefinition()
259
    {
260
        return $this->formObject->getDefinition()->getSteps()->getFirstStepDefinition();
261
    }
262
}
263