Completed
Push — wip/steps ( b7e8e2...f18ed0 )
by Romain
05:33
created

findFirstSubstepDefinition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 2
nc 2
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\Condition\Processor\ConditionProcessorFactory;
17
use Romm\Formz\Condition\Processor\DataObject\PhpConditionDataObject;
18
use Romm\Formz\Core\Core;
19
use Romm\Formz\Error\FormResult;
20
use Romm\Formz\Form\Definition\Step\Step\Step;
21
use Romm\Formz\Form\Definition\Step\Step\StepDefinition;
22
use Romm\Formz\Form\Definition\Step\Step\Substep\SubstepDefinition;
23
use Romm\Formz\Form\FormObject\FormObject;
24
use Romm\Formz\Form\FormObject\FormObjectFactory;
25
use Romm\Formz\Form\FormObject\Service\Step\FormStepPersistence;
26
use Romm\Formz\Middleware\Request\Redirect;
27
use Romm\Formz\Service\Traits\SelfInstantiateTrait;
28
use Romm\Formz\Validation\Form\DataObject\FormValidatorDataObject;
29
use Romm\Formz\Validation\Form\FormValidatorExecutor;
30
use TYPO3\CMS\Core\SingletonInterface;
31
use TYPO3\CMS\Extbase\Mvc\Web\Request;
32
33
/**
34
 * This service allows extended form steps manipulation.
35
 */
36
class StepMiddlewareService implements SingletonInterface
37
{
38
    use SelfInstantiateTrait;
39
40
    /**
41
     * @var FormObject
42
     */
43
    protected $formObject;
44
45
    /**
46
     * @var Request
47
     */
48
    protected $request;
49
50
    /**
51
     * @var StepMiddlewareValidationService
52
     */
53
    protected $validationService;
54
55
    /**
56
     * @var FormStepPersistence
57
     */
58
    protected $persistence;
59
60
    /**
61
     * @param FormObject $formObject
62
     * @param Request $request
63
     */
64
    public function reset(FormObject $formObject, Request $request)
65
    {
66
        $this->formObject = $formObject;
67
        $this->request = $request;
68
69
        $this->persistence = FormObjectFactory::get()->getStepService($formObject)->getStepPersistence();
70
71
        $this->validationService = Core::instantiate(StepMiddlewareValidationService::class, $this);
72
    }
73
74
    /**
75
     * @param Step $currentStep
76
     * @return StepDefinition|null
77
     */
78
    public function getNextStep(Step $currentStep)
79
    {
80
        /*
81
         * The form was submitted, and no error was found, we can safely
82
         * dispatch the request to the next step.
83
         */
84
        $currentStepDefinition = $this->getStepDefinition($currentStep);
85
86
        $this->validationService->markStepAsValidated($currentStepDefinition);
0 ignored issues
show
Bug introduced by
It seems like $currentStepDefinition defined by $this->getStepDefinition($currentStep) on line 84 can be null; however, Romm\Formz\Middleware\It...::markStepAsValidated() 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...
87
        // @todo tmp-delete?
88
//        // Saving submitted form data for further usage.
89
//        $this->markStepAsValidated($currentStepDefinition, $this->getFormRawValues());
90
        $this->addValidatedFields($this->formObject->getFormResult()->getValidatedFields());
91
92
        $nextStep = null;
93
94
        if ($currentStepDefinition->hasNextStep()
95
            || $currentStepDefinition->hasDivergence()
96
        ) {
97
            $nextStep = $this->getNextStepDefinition($currentStepDefinition);
0 ignored issues
show
Bug introduced by
It seems like $currentStepDefinition defined by $this->getStepDefinition($currentStep) on line 84 can be null; however, Romm\Formz\Middleware\It...getNextStepDefinition() 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...
98
        }
99
100
        return $nextStep;
101
    }
102
103
    // @todo tmp-delete?
104
//    /**
105
//     * Saves the submitted values in the metadata, for the given step.
106
//     *
107
//     * @param Step $currentStep
108
//     */
109
//    public function saveStepFormValues(Step $currentStep)
110
//    {
111
//        $this->persistence->addStepFormValues($this->getStepDefinition($currentStep), $this->getFormRawValues());
112
//    }
113
//
114
//    /**
115
//     * Fetches the raw values sent in the request.
116
//     *
117
//     * @return array
118
//     * @throws InvalidArgumentTypeException
119
//     */
120
//    protected function getFormRawValues()
121
//    {
122
//        $formName = $this->getFormObject()->getName();
123
//        $formArray = null;
124
//
125
//        if ($this->request->hasArgument($formName)) {
126
//            /** @var array $formArray */
127
//            $formArray = $this->request->getArgument($formName);
128
//
129
//            if (false === is_array($formArray)) {
130
//                throw InvalidArgumentTypeException::formArgumentNotArray($this->getFormObject(), $formArray);
131
//            }
132
//        } else {
133
//            $formArray = [];
134
//        }
135
//
136
//        return $formArray;
137
//    }
138
//
139
//    /**
140
//     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::markStepAsValidated()
141
//     *
142
//     * @param StepDefinition $stepDefinition
143
//     * @param array          $formValues
144
//     */
145
//    public function markStepAsValidated(StepDefinition $stepDefinition, array $formValues)
146
//    {
147
//        $this->validationService->markStepAsValidated($stepDefinition, $formValues);
148
//    }
149
150
    /**
151
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::addValidatedFields
152
     *
153
     * @param array $validatedFields
154
     */
155
    public function addValidatedFields(array $validatedFields)
156
    {
157
        $this->validationService->addValidatedFields($validatedFields);
158
    }
159
160
    /**
161
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::stepDefinitionIsValid
162
     *
163
     * @param StepDefinition $stepDefinition
164
     * @return bool
165
     */
166
    public function stepIsValid(StepDefinition $stepDefinition)
167
    {
168
        return $this->validationService->stepDefinitionIsValid($stepDefinition);
169
    }
170
171
    /**
172
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::getFirstInvalidStep
173
     *
174
     * @param Step $step
175
     * @return StepDefinition|null
176
     */
177
    public function getFirstInvalidStep(Step $step)
178
    {
179
        return $this->validationService->getFirstInvalidStep($step);
180
    }
181
182
    /**
183
     * @param StepDefinition $step
184
     * @return StepDefinition
185
     */
186
    public function getNextStepDefinition(StepDefinition $step)
187
    {
188
        $nextStep = null;
189
190
        if ($step->hasDivergence()) {
191
            $divergenceSteps = $step->getDivergenceSteps();
192
193
            foreach ($divergenceSteps as $divergenceStep) {
194
                if (true === $this->getStepDefinitionConditionResult($divergenceStep)) {
195
                    $nextStep = $divergenceStep;
196
                    break;
197
                }
198
            }
199
        }
200
201
        if (null === $nextStep) {
202
            while ($step->hasNextStep()) {
203
                $step = $step->getNextStep();
204
205
                if ($step->hasActivation()) {
206
                    if (true === $this->getStepDefinitionConditionResult($step)) {
207
                        $nextStep = $step;
208
                        break;
209
                    } else {
210
                        $this->persistence->removeStep($step);
211
                    }
212
                } else {
213
                    $nextStep = $step;
214
                    break;
215
                }
216
            }
217
        }
218
219
        return $nextStep;
220
    }
221
222
    public function getNextSubstepDefinition(SubstepDefinition $substepDefinition)
223
    {
224
        $nextSubstep = null;
225
226
        if ($substepDefinition->hasDivergence()) {
227
            $divergenceSteps = $substepDefinition->getDivergenceSubsteps();
228
229
            foreach ($divergenceSteps as $divergenceStep) {
230
                if (true === $this->getSubstepDefinitionConditionResult($divergenceStep)) {
231
                    $nextSubstep = $divergenceStep;
232
                    break;
233
                }
234
            }
235
        }
236
237
        if (null === $nextSubstep) {
238
            while ($substepDefinition->hasNextSubstep()) {
239
                $substepDefinition = $substepDefinition->getNextSubstep();
240
241
                if ($this->substepIsValid($substepDefinition)) {
242
                    $nextSubstep = $substepDefinition;
243
                }
244
            }
245
        }
246
247
        return $nextSubstep;
248
    }
249
250
    public function findSubstepDefinition(Step $step, callable $callback)
251
    {
252
        return $this->findSubstepDefinitionRecursive($step->getSubsteps()->getFirstSubstepDefinition(), $callback);
253
    }
254
255
    /**
256
     * Finds the first valid substep.
257
     *
258
     * @param Step $step
259
     * @return SubstepDefinition|null
260
     */
261
    public function findFirstSubstepDefinition(Step $step)
262
    {
263
        $firstSubstepDefinition = $step->getSubsteps()->getFirstSubstepDefinition();
264
265
        return $this->substepIsValid($firstSubstepDefinition)
266
            ? $firstSubstepDefinition
267
            : $this->getNextSubstepDefinition($firstSubstepDefinition);
268
    }
269
270
    protected function findSubstepDefinitionRecursive(SubstepDefinition $substepDefinition, callable $callback)
271
    {
272
        $result = $callback($substepDefinition);
273
274
        if (true === $result) {
275
            return $substepDefinition;
276
        }
277
278
        $substepDefinition = $this->getNextSubstepDefinition($substepDefinition);
279
280
        return $substepDefinition
281
            ? $this->findSubstepDefinitionRecursive($substepDefinition, $callback)
282
            : null;
283
    }
284
285
    /**
286
     * @param StepDefinition $stepDefinition
287
     * @param Redirect       $redirect
288
     */
289
    public function moveForwardToStep(StepDefinition $stepDefinition, Redirect $redirect)
290
    {
291
        $this->persistence->setStepLevel($stepDefinition);
292
        $this->redirectToStep($stepDefinition->getStep(), $redirect);
293
    }
294
295
    /**
296
     * Redirects the current request to the given step.
297
     *
298
     * @param Step     $step
299
     * @param Redirect $redirect
300
     */
301
    public function redirectToStep(Step $step, Redirect $redirect)
302
    {
303
        $redirect->toPage($step->getPageUid())
304
            ->toExtension($step->getExtension())
305
            ->toController($step->getController())
306
            ->toAction($step->getAction())
307
            ->withArguments([
308
                'fz-hash' => [
309
                    $this->formObject->getName() => $this->formObject->getFormHash()
310
                ]
311
            ])
312
            ->dispatch();
313
    }
314
315
    /**
316
     * @param StepDefinition $stepDefinition
317
     * @return bool
318
     */
319
    public function getStepDefinitionConditionResult(StepDefinition $stepDefinition)
320
    {
321
        $conditionProcessor = ConditionProcessorFactory::getInstance()->get($this->getFormObject());
322
        $tree = $conditionProcessor->getActivationConditionTreeForStep($stepDefinition);
323
        $todo = new FormValidatorExecutor(new FormValidatorDataObject($this->getFormObject(), new FormResult(), true)); // @todo
324
        $dataObject = new PhpConditionDataObject($this->getFormObject()->getForm(), $todo);
325
326
        return $tree->getPhpResult($dataObject);
327
    }
328
329
    /**
330
     * @param SubstepDefinition $substepDefinition
331
     * @return bool
332
     */
333
    public function getSubstepDefinitionConditionResult(SubstepDefinition $substepDefinition)
334
    {
335
        $conditionProcessor = ConditionProcessorFactory::getInstance()->get($this->getFormObject());
336
        $tree = $conditionProcessor->getActivationConditionTreeForSubstep($substepDefinition);
337
        $todo = new FormValidatorExecutor(new FormValidatorDataObject($this->getFormObject(), new FormResult(), true)); // @todo
338
        $dataObject = new PhpConditionDataObject($this->getFormObject()->getForm(), $todo);
339
340
        return $tree->getPhpResult($dataObject);
341
    }
342
343
    private function substepIsValid(SubstepDefinition $substepDefinition)
344
    {
345
        if ($substepDefinition->hasActivation()) {
346
            return $this->getSubstepDefinitionConditionResult($substepDefinition);
347
        }
348
349
        return true;
350
    }
351
352
    /**
353
     * @param Step $step
354
     * @return StepDefinition|null
355
     */
356
    public function getStepDefinition(Step $step)
357
    {
358
        return FormObjectFactory::get()
359
            ->getStepService($this->getFormObject())
360
            ->getStepDefinition($step);
361
    }
362
363
    /**
364
     * @return FormStepPersistence
365
     */
366
    public function getStepPersistence()
367
    {
368
        return $this->persistence;
369
    }
370
371
    /**
372
     * @return FormObject
373
     */
374
    public function getFormObject()
375
    {
376
        return $this->formObject;
377
    }
378
379
    /**
380
     * @return StepDefinition
381
     */
382
    public function getFirstStepDefinition()
383
    {
384
        return $this->formObject->getDefinition()->getSteps()->getFirstStepDefinition();
385
    }
386
}
387