Completed
Push — wip/steps-tmp ( ae03e8...c2561f )
by Romain
02:16
created

StepMiddlewareService   C

Complexity

Total Complexity 41

Size/Duplication

Total Lines 361
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
wmc 41
lcom 1
cbo 18
dl 0
loc 361
rs 6.1968
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A reset() 0 9 1
A getNextStep() 0 22 2
A addValidatedFields() 0 4 1
A stepIsValid() 0 4 1
A getFirstInvalidStep() 0 4 1
C getNextStepDefinition() 0 35 8
C getNextSubstepDefinition() 0 33 8
A findSubstepDefinition() 0 4 1
A findSubstepDefinitionRecursive() 0 14 3
A moveForwardToStep() 0 5 1
A redirectToStep() 0 13 1
A getStepDefinitionConditionResult() 0 9 1
A getSubstepDefinitionConditionResult() 0 9 1
A getStepDefinition() 0 4 1
C findStepDefinition() 0 26 7
A getStepPersistence() 0 4 1
A getFormObject() 0 4 1
A getFirstStepDefinition() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like StepMiddlewareService often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use StepMiddlewareService, and based on these observations, apply Extract Interface, too.

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\Form\Definition\Step\Step\Step;
20
use Romm\Formz\Form\Definition\Step\Step\StepDefinition;
21
use Romm\Formz\Form\Definition\Step\Step\Substep\SubstepDefinition;
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\Form\DataObject\FormValidatorDataObject;
28
use Romm\Formz\Validation\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
        $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
            $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...
96
        }
97
98
        return $nextStep;
99
    }
100
101
    // @todo tmp-delete?
102
//    /**
103
//     * Saves the submitted values in the metadata, for the given step.
104
//     *
105
//     * @param Step $currentStep
106
//     */
107
//    public function saveStepFormValues(Step $currentStep)
108
//    {
109
//        $this->persistence->addStepFormValues($this->getStepDefinition($currentStep), $this->getFormRawValues());
110
//    }
111
//
112
//    /**
113
//     * Fetches the raw values sent in the request.
114
//     *
115
//     * @return array
116
//     * @throws InvalidArgumentTypeException
117
//     */
118
//    protected function getFormRawValues()
119
//    {
120
//        $formName = $this->getFormObject()->getName();
121
//        $formArray = null;
122
//
123
//        if ($this->request->hasArgument($formName)) {
124
//            /** @var array $formArray */
125
//            $formArray = $this->request->getArgument($formName);
126
//
127
//            if (false === is_array($formArray)) {
128
//                throw InvalidArgumentTypeException::formArgumentNotArray($this->getFormObject(), $formArray);
129
//            }
130
//        } else {
131
//            $formArray = [];
132
//        }
133
//
134
//        return $formArray;
135
//    }
136
//
137
//    /**
138
//     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::markStepAsValidated()
139
//     *
140
//     * @param StepDefinition $stepDefinition
141
//     * @param array          $formValues
142
//     */
143
//    public function markStepAsValidated(StepDefinition $stepDefinition, array $formValues)
144
//    {
145
//        $this->validationService->markStepAsValidated($stepDefinition, $formValues);
146
//    }
147
148
    /**
149
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::addValidatedFields
150
     *
151
     * @param array $validatedFields
152
     */
153
    public function addValidatedFields(array $validatedFields)
154
    {
155
        $this->validationService->addValidatedFields($validatedFields);
156
    }
157
158
    /**
159
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::stepDefinitionIsValid
160
     *
161
     * @param StepDefinition $stepDefinition
162
     * @return bool
163
     */
164
    public function stepIsValid(StepDefinition $stepDefinition)
165
    {
166
        return $this->validationService->stepDefinitionIsValid($stepDefinition);
167
    }
168
169
    /**
170
     * @see \Romm\Formz\Middleware\Item\Step\Service\StepMiddlewareValidationService::getFirstInvalidStep
171
     *
172
     * @param Step $step
173
     * @return StepDefinition|null
174
     */
175
    public function getFirstInvalidStep(Step $step)
176
    {
177
        return $this->validationService->getFirstInvalidStep($step);
178
    }
179
180
    /**
181
     * @param StepDefinition $step
182
     * @return StepDefinition
183
     */
184
    public function getNextStepDefinition(StepDefinition $step)
185
    {
186
        $nextStep = null;
187
188
        if ($step->hasDivergence()) {
189
            $divergenceSteps = $step->getDivergenceSteps();
190
191
            foreach ($divergenceSteps as $divergenceStep) {
192
                if (true === $this->getStepDefinitionConditionResult($divergenceStep)) {
193
                    $nextStep = $divergenceStep;
194
                    break;
195
                }
196
            }
197
        }
198
199
        if (null === $nextStep) {
200
            while ($step->hasNextStep()) {
201
                $step = $step->getNextStep();
202
203
                if ($step->hasActivation()) {
204
                    if (true === $this->getStepDefinitionConditionResult($step)) {
205
                        $nextStep = $step;
206
                        break;
207
                    } else {
208
                        $this->persistence->removeStep($step);
209
                    }
210
                } else {
211
                    $nextStep = $step;
212
                    break;
213
                }
214
            }
215
        }
216
217
        return $nextStep;
218
    }
219
220
    public function getNextSubstepDefinition(SubstepDefinition $substepDefinition)
221
    {
222
        $nextSubstep = null;
223
224
        if ($substepDefinition->hasDivergence()) {
225
            $divergenceSteps = $substepDefinition->getDivergenceSubsteps();
226
227
            foreach ($divergenceSteps as $divergenceStep) {
228
                if (true === $this->getSubstepDefinitionConditionResult($divergenceStep)) {
229
                    $nextSubstep = $divergenceStep;
230
                    break;
231
                }
232
            }
233
        }
234
235
        if (null === $nextSubstep) {
236
            while ($substepDefinition->hasNextSubstep()) {
237
                $substepDefinition = $substepDefinition->getNextSubstep();
238
239
                if ($substepDefinition->hasActivation()) {
240
                    if (true === $this->getSubstepDefinitionConditionResult($substepDefinition)) {
241
                        $nextSubstep = $substepDefinition;
242
                        break;
243
                    }
244
                } else {
245
                    $nextSubstep = $substepDefinition;
246
                    break;
247
                }
248
            }
249
        }
250
251
        return $nextSubstep;
252
    }
253
254
    public function findSubstepDefinition(Step $step, callable $callback)
255
    {
256
        return $this->findSubstepDefinitionRecursive($step->getSubsteps()->getFirstSubstepDefinition(), $callback);
257
    }
258
259
    protected function findSubstepDefinitionRecursive(SubstepDefinition $substepDefinition, callable $callback)
260
    {
261
        $result = $callback($substepDefinition);
262
263
        if (true === $result) {
264
            return $substepDefinition;
265
        }
266
267
        $substepDefinition = $this->getNextSubstepDefinition($substepDefinition);
268
269
        return $substepDefinition
270
            ? $this->findSubstepDefinitionRecursive($substepDefinition, $callback)
271
            : null;
272
    }
273
274
    /**
275
     * @param StepDefinition $stepDefinition
276
     * @param Redirect       $redirect
277
     */
278
    public function moveForwardToStep(StepDefinition $stepDefinition, Redirect $redirect)
279
    {
280
        $this->persistence->setStepLevel($stepDefinition);
281
        $this->redirectToStep($stepDefinition->getStep(), $redirect);
282
    }
283
284
    /**
285
     * Redirects the current request to the given step.
286
     *
287
     * @param Step     $step
288
     * @param Redirect $redirect
289
     */
290
    public function redirectToStep(Step $step, Redirect $redirect)
291
    {
292
        $redirect->toPage($step->getPageUid())
293
            ->toExtension($step->getExtension())
294
            ->toController($step->getController())
295
            ->toAction($step->getAction())
296
            ->withArguments([
297
                'fz-hash' => [
298
                    $this->formObject->getName() => $this->formObject->getFormHash()
299
                ]
300
            ])
301
            ->dispatch();
302
    }
303
304
    /**
305
     * @param StepDefinition $stepDefinition
306
     * @return bool
307
     */
308
    public function getStepDefinitionConditionResult(StepDefinition $stepDefinition)
309
    {
310
        $conditionProcessor = ConditionProcessorFactory::getInstance()->get($this->getFormObject());
311
        $tree = $conditionProcessor->getActivationConditionTreeForStep($stepDefinition);
312
        $todo = new FormValidatorExecutor(new FormValidatorDataObject($this->getFormObject(), new FormResult(), true)); // @todo
313
        $dataObject = new PhpConditionDataObject($this->getFormObject()->getForm(), $todo);
314
315
        return $tree->getPhpResult($dataObject);
316
    }
317
318
    /**
319
     * @param SubstepDefinition $substepDefinition
320
     * @return bool
321
     */
322
    public function getSubstepDefinitionConditionResult(SubstepDefinition $substepDefinition)
323
    {
324
        $conditionProcessor = ConditionProcessorFactory::getInstance()->get($this->getFormObject());
325
        $tree = $conditionProcessor->getActivationConditionTreeForSubstep($substepDefinition);
326
        $todo = new FormValidatorExecutor(new FormValidatorDataObject($this->getFormObject(), new FormResult(), true)); // @todo
327
        $dataObject = new PhpConditionDataObject($this->getFormObject()->getForm(), $todo);
328
329
        return $tree->getPhpResult($dataObject);
330
    }
331
332
    /**
333
     * @param Step $step
334
     * @return StepDefinition|null
335
     */
336
    public function getStepDefinition(Step $step)
337
    {
338
        return $this->findStepDefinition($step, $this->getFirstStepDefinition());
339
    }
340
341
    /**
342
     * @param Step           $step
343
     * @param StepDefinition $stepDefinition
344
     * @return StepDefinition|null
345
     */
346
    protected function findStepDefinition(Step $step, StepDefinition $stepDefinition)
347
    {
348
        if ($stepDefinition->getStep() === $step) {
349
            return $stepDefinition;
350
        }
351
352
        if ($stepDefinition->hasNextStep()) {
353
            $result = $this->findStepDefinition($step, $stepDefinition->getNextStep());
354
355
            if ($result instanceof StepDefinition) {
356
                return $result;
357
            }
358
        }
359
360
        if ($stepDefinition->hasDivergence()) {
361
            foreach ($stepDefinition->getDivergenceSteps() as $divergenceStep) {
362
                $result = $this->findStepDefinition($step, $divergenceStep);
363
364
                if ($result instanceof StepDefinition) {
365
                    return $result;
366
                }
367
            }
368
        }
369
370
        return null;
371
    }
372
373
    /**
374
     * @return FormStepPersistence
375
     */
376
    public function getStepPersistence()
377
    {
378
        return $this->persistence;
379
    }
380
381
    /**
382
     * @return FormObject
383
     */
384
    public function getFormObject()
385
    {
386
        return $this->formObject;
387
    }
388
389
    /**
390
     * @return StepDefinition
391
     */
392
    public function getFirstStepDefinition()
393
    {
394
        return $this->formObject->getDefinition()->getSteps()->getFirstStepDefinition();
395
    }
396
}
397