Completed
Push — middleware-wip ( fd2413...a8f94d )
by Romain
22:23
created

StepMiddlewareService::getStepPersistence()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
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\Middleware\Item\Step\Service;
15
16
use Romm\Formz\Configuration\Form\Step\Step\Step;
17
use Romm\Formz\Configuration\Form\Step\Step\StepDefinition;
18
use Romm\Formz\Core\Core;
19
use Romm\Formz\Error\FormResult;
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
     * Checks that the previous step has already been validated, meaning the
70
     * user has the right to stand in the given step.
71
     *
72
     * @param Step $step
73
     * @return bool
74
     */
75
    public function stepIsValid(Step $step)
76
    {
77
        $stepDefinition = $this->getStepDefinition($step);
78
79
        if (false === $stepDefinition->hasPreviousDefinition()) {
80
            /*
81
             * No previous step definition found: the user stands on the first
82
             * step, it always has the right to stand there.
83
             */
84
            return true;
85
        }
86
87
        $previousStep = $stepDefinition->getPreviousDefinition()->getStep();
88
89
        return $this->getStepPersistence()->stepWasValidated($previousStep);
90
    }
91
92
    /**
93
     * Searches for the first invalid step among previous steps from the given
94
     * step.
95
     *
96
     * All previous steps are listed, then for each one we check if submitted
97
     * form values has been saved in the step persistence, in which case the
98
     * step validation is launched again with the current form configuration.
99
     *
100
     * @param Step $step
101
     * @return Step|null
102
     */
103
    public function getFirstInvalidStep(Step $step)
104
    {
105
        $firstStep = $this->getFirstStepDefinition()->getStep();
106
107
        if ($step === $firstStep) {
108
            /*
109
             * The first step is always valid.
110
             */
111
            return null;
112
        }
113
114
        /*
115
         * If there is no form instance, and the request is not in the first
116
         * step, obviously the user should not be there.
117
         */
118
        if (false === $this->formObject->hasForm()) {
119
            return $firstStep;
120
        }
121
122
        /** @var StepDefinition[] $stepDefinitionsToTest */
123
        $stepDefinitionsToTest = [];
124
        $invalidStep = null;
125
        $stepDefinition = $this->getStepDefinition($step);
126
127
        while ($stepDefinition->hasPreviousDefinition()) {
128
            $stepDefinition = $stepDefinition->getPreviousDefinition();
129
            array_unshift($stepDefinitionsToTest, $stepDefinition);
130
        }
131
132
        foreach ($stepDefinitionsToTest as $stepDefinition) {
133
            $step = $stepDefinition->getStep();
134
135
            /*
136
             * If the already submitted form values are not found, the step is
137
             * considered as invalid.
138
             */
139
            if (false === $this->getStepPersistence()->hasStepFormValues($step)) {
140
                $invalidStep = $step;
141
                break;
142
            }
143
144
            $result = $this->validateStep($step);
145
146
            if ($result->hasErrors()) {
147
                $invalidStep = $step;
148
                break;
149
            } else {
150
                $this->getStepPersistence()->markStepAsValidated($step);
151
            }
152
        }
153
154
        return $invalidStep;
155
    }
156
157
    /**
158
     * Redirects the current request to the given step.
159
     *
160
     * @param Step     $step
161
     * @param Redirect $redirect
162
     */
163
    public function redirectToStep(Step $step, Redirect $redirect)
164
    {
165
        $redirect->toPage($step->getPageUid())
166
            ->toExtension($step->getExtension())
167
            ->toController($step->getController())
168
            ->toAction($step->getAction())
169
            ->withArguments([
170
                'fz-hash' => [
171
                    $this->formObject->getName() => $this->formObject->getFormHash()
172
                ]
173
            ])
174
            ->dispatch();
175
    }
176
177
    /**
178
     * @param Step $step
179
     * @return StepDefinition|null
180
     */
181
    public function getStepDefinition(Step $step)
182
    {
183
        $stepDefinition = $this->getFirstStepDefinition();
184
185
        while ($stepDefinition) {
186
            if ($stepDefinition->getStep() === $step) {
187
                return $stepDefinition;
188
            }
189
190
            $stepDefinition = $stepDefinition->hasNextSteps()
191
                ? $stepDefinition->getNextSteps()->getDefaultStep()
192
                : null;
193
        }
194
195
        return null;
196
    }
197
198
    /**
199
     * Validates (again) the given step with the form data that were previously
200
     * submitted and fetched from the step persistence.
201
     *
202
     * @param Step $step
203
     * @return FormResult
204
     */
205
    protected function validateStep(Step $step)
206
    {
207
        /** @var FormValidationMiddlewareOption $formValidationMiddlewareOptions */
208
        $formValidationMiddlewareOptions = $this->formObject
209
            ->getConfiguration()
210
            ->getPresetMiddlewares()
211
            ->getFormValidationMiddleware()
212
            ->getOptions();
213
214
        /** @var AbstractFormValidator $validator */
215
        $validator = Core::instantiate(
216
            $formValidationMiddlewareOptions->getFormValidatorClassName(),
217
            [
218
                'name'  => $this->formObject->getName(),
219
                'dummy' => true
220
            ]
221
        );
222
223
        $validator->getDataObject()->setValidatedStep($step);
224
225
        /** @var PropertyMapper $propertyMapper */
226
        $propertyMapper = Core::instantiate(PropertyMapper::class);
227
        $form = $propertyMapper->convert($this->getStepPersistence()->getStepFormValues($step), $this->formObject->getClassName());
228
229
        return $validator->validate($form);
230
    }
231
232
    /**
233
     * @return FormStepPersistence
234
     */
235
    protected function getStepPersistence()
236
    {
237
        if (null === $this->formStepPersistence) {
238
            $proxy = FormObjectFactory::get()->getProxy($this->formObject->getForm());
239
240
            $this->formStepPersistence = $proxy->getStepPersistence();
241
        }
242
243
        return $this->formStepPersistence;
244
    }
245
246
    /**
247
     * @return StepDefinition
248
     */
249
    protected function getFirstStepDefinition()
250
    {
251
        return $this->formObject->getConfiguration()->getSteps()->getFirstStepDefinition();
252
    }
253
}
254