Completed
Push — wip/steps ( 47b00f...fe9b9d )
by Romain
02:49
created

StepMiddlewareService::saveStepFormValues()   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\Middleware\Item\Step\Service;
15
16
use Romm\Formz\Condition\Processor\ConditionProcessorFactory;
17
use Romm\Formz\Condition\Processor\DataObject\PhpConditionDataObject;
18
use Romm\Formz\Error\FormResult;
19
use Romm\Formz\Exceptions\InvalidArgumentTypeException;
20
use Romm\Formz\Form\Definition\Step\Step\Step;
21
use Romm\Formz\Form\Definition\Step\Step\StepDefinition;
22
use Romm\Formz\Form\FormObject\FormObject;
23
use Romm\Formz\Form\FormObject\FormObjectFactory;
24
use Romm\Formz\Form\FormObject\Service\Step\FormStepPersistence;
25
use Romm\Formz\Middleware\Request\Redirect;
26
use Romm\Formz\Service\Traits\SelfInstantiateTrait;
27
use Romm\Formz\Validation\Validator\Form\DataObject\FormValidatorDataObject;
28
use Romm\Formz\Validation\Validator\Form\FormValidatorExecutor;
29
use TYPO3\CMS\Core\SingletonInterface;
30
use TYPO3\CMS\Core\Utility\GeneralUtility;
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 = GeneralUtility::makeInstance(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
        // Saving submitted form data for further usage.
87
        $this->markStepAsValidated($currentStepDefinition, $this->getFormRawValues());
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...
88
        $this->addValidatedFields($this->formObject->getFormResult()->getValidatedFields());
89
90
        $nextStep = null;
91
92
        if ($currentStepDefinition->hasNextStep()) {
93
            $nextStep = $this->getNextStepDefinition($currentStepDefinition, true);
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...
94
        }
95
96
        return $nextStep;
97
    }
98
99
    /**
100
     * Saves the submitted values in the metadata, for the given step.
101
     *
102
     * @param Step $currentStep
103
     */
104
    public function saveStepFormValues(Step $currentStep)
105
    {
106
        $this->persistence->addStepFormValues($this->getStepDefinition($currentStep), $this->getFormRawValues());
0 ignored issues
show
Bug introduced by
It seems like $this->getStepDefinition($currentStep) can be null; however, addStepFormValues() 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...
107
    }
108
109
    /**
110
     * Fetches the raw values sent in the request.
111
     *
112
     * @return array
113
     * @throws InvalidArgumentTypeException
114
     */
115
    protected function getFormRawValues()
116
    {
117
        $formName = $this->getFormObject()->getName();
118
        $formArray = null;
0 ignored issues
show
Unused Code introduced by
$formArray is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
119
120
        if ($this->request->hasArgument($formName)) {
121
            /** @var array $formArray */
122
            $formArray = $this->request->getArgument($formName);
123
124
            if (false === is_array($formArray)) {
125
                throw InvalidArgumentTypeException::formArgumentNotArray($this->getFormObject(), $formArray);
126
            }
127
        } else {
128
            $formArray = [];
129
        }
130
131
        return $formArray;
132
    }
133
134
    /**
135
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::markStepAsValidated
136
     *
137
     * @param StepDefinition $stepDefinition
138
     * @param array          $formValues
139
     */
140
    public function markStepAsValidated(StepDefinition $stepDefinition, array $formValues)
141
    {
142
        $this->validationService->markStepAsValidated($stepDefinition, $formValues);
143
    }
144
145
    /**
146
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::addValidatedFields
147
     *
148
     * @param array $validatedFields
149
     */
150
    public function addValidatedFields(array $validatedFields)
151
    {
152
        $this->validationService->addValidatedFields($validatedFields);
153
    }
154
155
    /**
156
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::stepDefinitionIsValid
157
     *
158
     * @param StepDefinition $stepDefinition
159
     * @return bool
160
     */
161
    public function stepIsValid(StepDefinition $stepDefinition)
162
    {
163
        return $this->validationService->stepDefinitionIsValid($stepDefinition);
164
    }
165
166
    /**
167
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::getFirstInvalidStep
168
     *
169
     * @param Step $step
170
     * @return StepDefinition|null
171
     */
172
    public function getFirstInvalidStep(Step $step)
173
    {
174
        return $this->validationService->getFirstInvalidStep($step);
175
    }
176
177
    /**
178
     * @param StepDefinition $step
179
     * @param bool           $removeConditionalSteps
180
     * @return StepDefinition
181
     */
182
    public function getNextStepDefinition(StepDefinition $step, $removeConditionalSteps = false)
183
    {
184
        $nextStep = null;
185
186
        if ($step->hasDivergence()) {
187
            $divergenceSteps = $step->getDivergenceSteps();
188
189
            foreach ($divergenceSteps as $divergenceStep) {
190
                if (true === $this->getStepDefinitionConditionResult($divergenceStep)) {
191
                    $nextStep = $divergenceStep;
192
                    break;
193
                }
194
            }
195
        }
196
197
        if (null === $nextStep) {
198
            while ($step->hasNextStep()) {
199
                $step = $step->getNextStep();
200
201
                if ($step->hasActivation()) {
202
                    if (true === $this->getStepDefinitionConditionResult($step)) {
203
                        $nextStep = $step;
204
                        break;
205
                    } elseif (true === $removeConditionalSteps) {
206
                        $this->persistence->removeStep($step);
207
                    }
208
                } else {
209
                    $nextStep = $step;
210
                    break;
211
                }
212
            }
213
        }
214
215
        return $nextStep;
216
    }
217
218
    /**
219
     * @param StepDefinition $stepDefinition
220
     * @param Redirect       $redirect
221
     */
222
    public function moveForwardToStep(StepDefinition $stepDefinition, Redirect $redirect)
223
    {
224
        $this->persistence->setStepLevel($stepDefinition);
225
        $this->redirectToStep($stepDefinition->getStep(), $redirect);
226
    }
227
228
    /**
229
     * Redirects the current request to the given step.
230
     *
231
     * @param Step     $step
232
     * @param Redirect $redirect
233
     */
234
    public function redirectToStep(Step $step, Redirect $redirect)
235
    {
236
        $redirect->toPage($step->getPageUid())
237
            ->toExtension($step->getExtension())
238
            ->toController($step->getController())
239
            ->toAction($step->getAction())
240
            ->withArguments([
241
                'fz-hash' => [
242
                    $this->formObject->getName() => $this->formObject->getFormHash()
243
                ]
244
            ])
245
            ->dispatch();
246
    }
247
248
    /**
249
     * @param StepDefinition $stepDefinition
250
     * @return bool
251
     */
252
    public function getStepDefinitionConditionResult(StepDefinition $stepDefinition)
253
    {
254
        $conditionProcessor = ConditionProcessorFactory::getInstance()->get($this->getFormObject());
255
        $tree = $conditionProcessor->getActivationConditionTreeForStep($stepDefinition);
256
        $todo = new FormValidatorExecutor($this->getFormObject(), new FormValidatorDataObject(new FormResult())); // @todo
257
        $dataObject = new PhpConditionDataObject($this->getFormObject()->getForm(), $todo);
258
259
        return $tree->getPhpResult($dataObject);
260
    }
261
262
    /**
263
     * @param Step $step
264
     * @return StepDefinition|null
265
     */
266
    public function getStepDefinition(Step $step)
267
    {
268
        return $this->findStepDefinition($step, $this->getFirstStepDefinition());
269
    }
270
271
    /**
272
     * @param Step           $step
273
     * @param StepDefinition $stepDefinition
274
     * @return StepDefinition|null
275
     */
276
    protected function findStepDefinition(Step $step, StepDefinition $stepDefinition)
277
    {
278
        if ($stepDefinition->getStep() === $step) {
279
            return $stepDefinition;
280
        }
281
282
        if ($stepDefinition->hasNextStep()) {
283
            $result = $this->findStepDefinition($step, $stepDefinition->getNextStep());
284
285
            if ($result instanceof StepDefinition) {
286
                return $result;
287
            }
288
        }
289
290
        if ($stepDefinition->hasDivergence()) {
291
            foreach ($stepDefinition->getDivergenceSteps() as $divergenceStep) {
292
                $result = $this->findStepDefinition($step, $divergenceStep);
293
294
                if ($result instanceof StepDefinition) {
295
                    return $result;
296
                }
297
            }
298
        }
299
300
        return null;
301
    }
302
303
    /**
304
     * @return FormStepPersistence
305
     */
306
    public function getStepPersistence()
307
    {
308
        return $this->persistence;
309
    }
310
311
    /**
312
     * @return FormObject
313
     */
314
    public function getFormObject()
315
    {
316
        return $this->formObject;
317
    }
318
319
    /**
320
     * @return StepDefinition
321
     */
322
    public function getFirstStepDefinition()
323
    {
324
        return $this->formObject->getDefinition()->getSteps()->getFirstStepDefinition();
325
    }
326
}
327