Completed
Push — wip/steps ( 1506fd...4499a9 )
by
unknown
08:43
created

getFirstNotValidatedStep()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
cc 3
nc 3
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\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\Service\Step\FormStepPersistence;
22
use Romm\Formz\Middleware\Item\FormValidation\FormValidationMiddlewareOption;
23
use Romm\Formz\Validation\Validator\Form\AbstractFormValidator;
24
use TYPO3\CMS\Extbase\SignalSlot\Dispatcher;
25
26
class StepMiddlewareValidationService
27
{
28
    const STEP_HAS_ERROR = 'STEP_HAS_ERROR';
29
30
    const STEP_INVALID_ACTIVATION = 'STEP_INVALID_ACTIVATION';
31
32
    /**
33
     * @var FormObject
34
     */
35
    protected $formObject;
36
37
    /**
38
     * @var StepMiddlewareService
39
     */
40
    protected $service;
41
42
    /**
43
     * @var FormStepPersistence
44
     */
45
    protected $persistence;
46
47
    /**
48
     * @var Dispatcher
49
     */
50
    protected $signalSlotDispatcher;
51
52
    /**
53
     * @param StepMiddlewareService $service
54
     * @param Dispatcher $signalSlotDispatcher
55
     */
56
    public function __construct(StepMiddlewareService $service, Dispatcher $signalSlotDispatcher)
57
    {
58
        $this->service = $service;
59
        $this->signalSlotDispatcher = $signalSlotDispatcher;
60
        $this->formObject = $service->getFormObject();
61
        $this->persistence = $service->getStepPersistence();
62
    }
63
64
    /**
65
     * Marks the given step as validated: no errors were found during validation
66
     * with the given values array.
67
     *
68
     * @param StepDefinition $stepDefinition
69
     * @param array $formValues
0 ignored issues
show
Bug introduced by
There is no parameter named $formValues. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
70
     */
71
    public function markStepAsValidated(StepDefinition $stepDefinition/*, array $formValues*/) // @todo tmp-delete?
72
    {
73
        $this->persistence->markStepAsValidated($stepDefinition);
74
75
        // @todo tmp-delete?
76
//        if ($this->persistence->hasStepFormValues($stepDefinition)
77
//            && serialize($formValues) !== serialize($this->persistence->getStepFormValues($stepDefinition))
78
//        ) {
79
//            $this->persistence->resetValidationData();
80
//        }
81
    }
82
83
    /**
84
     * @param array $validatedFields
85
     */
86
    public function addValidatedFields(array $validatedFields)
87
    {
88
        $this->persistence->addValidatedFields($validatedFields);
89
    }
90
91
    /**
92
     * Checks that the previous step has already been validated, meaning the
93
     * user has the right to stand in the given step.
94
     *
95
     * @param StepDefinition $stepDefinition
96
     * @return bool
97
     */
98
    public function stepDefinitionIsValid(StepDefinition $stepDefinition)
99
    {
100
        if (false === $stepDefinition->hasPreviousDefinition()) {
101
            /*
102
             * No previous step definition found: the user stands on the first
103
             * step, it always has the right to stand there.
104
             */
105
            return true;
106
        }
107
108
        $previousStep = $stepDefinition->getPreviousDefinition()->getStep();
109
        $stepLevel = $stepDefinition->getStepLevel();
110
111
        return $this->persistence->stepWasValidated($previousStep)
112
            && true === $this->persistence->hasStepIdentifierAtLevel($stepLevel)
113
            && $stepDefinition->getStep()->getIdentifier() === $this->persistence->getStepIdentifierAtLevel($stepLevel);
114
    }
115
116
    /**
117
     * Get the first not validated step
118
     *
119
     * @param StepDefinition $stepDefinition
120
     * @return StepDefinition|null
121
     */
122
    public function getFirstNotValidatedStep(StepDefinition $stepDefinition)
123
    {
124
        if (false === $stepDefinition->hasPreviousDefinition()) {
125
            /*
126
             * No previous step definition found: the user stands on the first
127
             * step, it always has the right to stand there.
128
             */
129
            return null;
130
        }
131
132
        $previousStepDefinition = $stepDefinition->getPreviousDefinition();
133
134
        if ($this->persistence->stepWasValidated($previousStepDefinition->getStep())) {
135
            return $stepDefinition;
136
        }
137
138
        return $this->getFirstNotValidatedStep($previousStepDefinition);
139
    }
140
141
    /**
142
     * Searches for the first invalid step among previous steps from the given
143
     * step.
144
     *
145
     * All previous steps are listed, then for each one we check if submitted
146
     * form values has been saved in the step persistence, in which case the
147
     * step validation is launched again with the current form configuration.
148
     *
149
     * @param Step $step
150
     * @return StepDefinition|null
151
     */
152
    public function getFirstInvalidStep(Step $step)
153
    {
154
        $firstStep = $this->service->getFirstStepDefinition();
155
        if ($step === $firstStep->getStep()) {
156
            /*
157
             * The first step is always valid.
158
             */
159
            return null;
160
        }
161
162
        /*
163
         * If there is no form instance, and the request is not in the first
164
         * step, obviously the user should not be there.
165
         */
166
        if (false === $this->formObject->hasForm()) {
167
            return $firstStep;
168
        }
169
170
        /** @var StepDefinition[] $stepDefinitionsToTest */
171
        $stepDefinitionsToTest = [];
172
        $invalidStepDefinition = null;
173
        $currentStepDefinition = $stepDefinition = $this->service->getStepDefinition($step);
174
175
        while ($stepDefinition->hasPreviousDefinition()) {
176
            $stepDefinition = $stepDefinition->getPreviousDefinition();
177
178
            if ($stepDefinition->hasActivation()) {
179
                if (true === $this->service->getStepDefinitionConditionResult($stepDefinition)) {
180
                    array_unshift($stepDefinitionsToTest, $stepDefinition);
181
                }
182
            } else {
183
                array_unshift($stepDefinitionsToTest, $stepDefinition);
184
            }
185
        }
186
187
        foreach ($stepDefinitionsToTest as $stepDefinition) {
188
189
            $step = $stepDefinition->getStep();
190
191
            /*
192
             * If the form was already validated, no need to do it again.
193
             */
194
            if ($this->persistence->stepWasValidated($step)) {
195
                continue;
196
            }
197
198
            $result = $this->validateStep($step);
199
200
            if ($result->hasErrors()) {
201
                $invalidStepDefinition = $stepDefinition;
202
203
                $this->signalSlotDispatcher->dispatch(
204
                    self::class,
205
                    self::STEP_HAS_ERROR,
206
                    [
207
                        $this->formObject,
208
                        $currentStepDefinition,
209
                        $invalidStepDefinition,
210
                        $result,
211
                    ]
212
                );
213
214
                break;
215
            }
216
        }
217
218
        if (null === $invalidStepDefinition
219
            && $currentStepDefinition->hasActivation()
220
            && false === $this->service->getStepDefinitionConditionResult($currentStepDefinition)
0 ignored issues
show
Bug introduced by
It seems like $currentStepDefinition defined by $stepDefinition = $this-...etStepDefinition($step) on line 173 can be null; however, Romm\Formz\Middleware\It...nitionConditionResult() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
221
        ) {
222
            $invalidStepDefinition = end($stepDefinitionsToTest);
223
224
            if (false === $invalidStepDefinition) {
225
                return null;
226
            }
227
228
            $this->signalSlotDispatcher->dispatch(
229
                self::class,
230
                self::STEP_INVALID_ACTIVATION,
231
                [
232
                    $this->formObject,
233
                    $currentStepDefinition,
234
                    $invalidStepDefinition,
235
                ]
236
            );
237
        }
238
        
239
        return $invalidStepDefinition;
240
    }
241
242
    /**
243
     * Validates (again) the given step with the form data that were previously
244
     * submitted and fetched from the step persistence.
245
     *
246
     * @param Step $step
247
     * @return FormResult
248
     */
249
    protected function validateStep(Step $step)
250
    {
251
        $form = $this->formObject->getForm();
252
253
        /** @var FormValidationMiddlewareOption $formValidationMiddlewareOptions */
254
        $formValidationMiddlewareOptions = $this->formObject
255
            ->getDefinition()
256
            ->getPresetMiddlewares()
257
            ->getFormValidationMiddleware()
258
            ->getOptions();
259
260
        /** @var AbstractFormValidator $validator */
261
        $validator = Core::instantiate(
262
            $formValidationMiddlewareOptions->getFormValidatorClassName(),
263
            [
264
                'name' => $this->formObject->getName(),
265
                'form' => $form,
266
                'dummy' => true
267
            ]
268
        );
269
270
        $validator->getDataObject()->setValidatedStep($step);
271
272
        return $validator->validate($form);
273
    }
274
}
275