Completed
Push — wip/steps ( 376e05...503a45 )
by Romain
04:05
created

ControllerProcessor::getRequestForms()   C

Complexity

Conditions 7
Paths 2

Size

Total Lines 29
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 29
rs 6.7272
cc 7
eloc 15
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\Controller\Processor;
15
16
use Romm\Formz\Form\FormInterface;
17
use Romm\Formz\Form\FormObject\FormObject;
18
use Romm\Formz\Form\FormObject\FormObjectFactory;
19
use Romm\Formz\Middleware\Scope\ScopeInterface;
20
use Romm\Formz\Service\Traits\ExtendedSelfInstantiateTrait;
21
use TYPO3\CMS\Core\SingletonInterface;
22
use TYPO3\CMS\Extbase\Mvc\Controller\Argument;
23
use TYPO3\CMS\Extbase\Mvc\Controller\Arguments;
24
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
25
use TYPO3\CMS\Extbase\Mvc\Request as MvcRequest;
26
use TYPO3\CMS\Extbase\Mvc\Web\Request;
27
28
class ControllerProcessor implements SingletonInterface
29
{
30
    use ExtendedSelfInstantiateTrait;
31
32
    /**
33
     * @var FormObjectFactory
34
     */
35
    protected $formObjectFactory;
36
37
    /**
38
     * @var FormObject[]
39
     */
40
    protected $formArguments;
41
42
    /**
43
     * @var bool
44
     */
45
    protected $dispatched = false;
46
47
    /**
48
     * @var Request
49
     */
50
    protected $originalRequest;
51
52
    /**
53
     * @var Request
54
     */
55
    protected $request;
56
57
    /**
58
     * @var Arguments
59
     */
60
    protected $requestArguments;
61
62
    /**
63
     * An interface name that does implement:
64
     *
65
     * @see \Romm\Formz\Middleware\Scope\ScopeInterface
66
     *
67
     * It will be used to filter the middlewares that will be called.
68
     *
69
     * @var string
70
     */
71
    protected $scope;
72
73
    /**
74
     * @var array
75
     */
76
    protected $settings = [];
77
78
    /**
79
     * @todo
80
     *
81
     * @var string
82
     */
83
    protected $lastDispatchedRequest;
84
85
    /**
86
     * @var callable
87
     */
88
    protected $exceptionCallback;
89
90
    /**
91
     * @param MvcRequest $request
92
     * @param Arguments  $requestArguments
93
     * @param array      $settings
94
     * @param string     $scope
95
     * @return $this
96
     */
97
    public static function prepare(MvcRequest $request, Arguments $requestArguments, $scope, array $settings = [])
98
    {
99
        return self::get()->setData($request, $requestArguments, $scope, $settings);
100
    }
101
102
    /**
103
     * Injects the data needed for this class to work properly. This method must
104
     * be called before the dispatch is called.
105
     *
106
     * @param MvcRequest $request
107
     * @param Arguments  $requestArguments
108
     * @param string     $scope
109
     * @param array      $settings
110
     * @return $this
111
     */
112
    public function setData(MvcRequest $request, Arguments $requestArguments, $scope, array $settings = [])
113
    {
114
        if (false === in_array(ScopeInterface::class, class_implements($scope))) {
115
            throw new \Exception('todo scope : ' . $scope); // @todo
116
        }
117
118
        /** @var Request $request */
119
        $dispatchedRequest = $request->getControllerObjectName() . '::' . $request->getControllerActionName() . '::' . $scope;
120
121
        if ($dispatchedRequest !== $this->lastDispatchedRequest) {
122
            $this->lastDispatchedRequest = $dispatchedRequest;
123
124
            $this->originalRequest = $request;
125
            $this->request = clone $request;
126
            $this->requestArguments = $requestArguments;
127
            $this->scope = $scope;
128
            $this->settings = $settings;
129
            $this->formArguments = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<Rom...FormObject\FormObject>> of property $formArguments.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
130
            $this->dispatched = false;
131
        }
132
133
        return $this;
134
    }
135
136
    /**
137
     * Will dispatch the current request to the form controller, which will take
138
     * care of processing everything properly.
139
     *
140
     * In case no form is found in the controller action parameters, the current
141
     * request is not killed.
142
     *
143
     * @throws StopActionException
144
     */
145
    public function dispatch()
146
    {
147
        if (false === $this->dispatched) {
148
            $this->dispatched = true;
149
150
            $this->doDispatch();
151
        }
152
    }
153
154
    /**
155
     * Wrapper for unit testing.
156
     */
157
    protected function doDispatch()
158
    {
159
        if (false === empty($this->getRequestForms())) {
160
            $this->originalRequest->setDispatched(false);
161
            $this->originalRequest->setControllerVendorName('Romm');
162
            $this->originalRequest->setControllerExtensionName('Formz');
163
            $this->originalRequest->setControllerName('Form');
164
            $this->originalRequest->setControllerActionName('processForm');
165
166
            $this->checkFormObjectsErrors();
167
168
            throw new StopActionException;
169
        }
170
    }
171
172
    /**
173
     * Will check if the form objects found in the request arguments contain
174
     * configuration errors. If they do, we dispatch the request to the error
175
     * view, where all errors will be explained properly to the user.
176
     */
177
    protected function checkFormObjectsErrors()
178
    {
179
        foreach ($this->getRequestForms() as $formObject) {
180
            if ($formObject->getDefinitionValidationResult()->hasErrors()) {
181
                $this->originalRequest->setControllerActionName('formObjectError');
182
                $this->originalRequest->setArguments(['formObject' => $formObject]);
183
184
                break;
185
            }
186
        }
187
    }
188
189
    /**
190
     * Loops on the request arguments, and pick up each one that is a form
191
     * instance (it implements `FormInterface`).
192
     *
193
     * @return FormObject[]
194
     */
195
    public function getRequestForms()
196
    {
197
        if (null === $this->formArguments) {
198
            $this->formArguments = [];
199
200
            /** @var Argument $argument */
201
            foreach ($this->requestArguments as $argument) {
202
                $type = $argument->getDataType();
203
204
                if ($this->request->hasArgument($argument->getName())
205
                    && $this->request->getArgument($argument->getName()) instanceof $type
206
                ) {
207
                    continue;
208
                }
209
210
                if (class_exists($type)
211
                    && in_array(FormInterface::class, class_implements($type))
212
                ) {
213
                    $formClassName = $argument->getDataType();
214
                    $formName = $argument->getName();
215
216
                    $formObject = $this->formObjectFactory->getInstanceWithClassName($formClassName, $formName);
217
                    $this->formArguments[$formName] = $formObject;
218
                }
219
            }
220
        }
221
222
        return $this->formArguments;
223
    }
224
225
    /**
226
     * @return Request
227
     */
228
    public function getRequest()
229
    {
230
        return $this->request;
231
    }
232
233
    /**
234
     * @return Arguments
235
     */
236
    public function getRequestArguments()
237
    {
238
        return $this->requestArguments;
239
    }
240
241
    /**
242
     * @return array
243
     */
244
    public function getSettings()
245
    {
246
        return $this->settings;
247
    }
248
249
    /**
250
     * @return string
251
     */
252
    public function getScope()
253
    {
254
        return $this->scope;
255
    }
256
257
    /**
258
     * @param callable $callback
259
     * @return $this
260
     */
261
    public function setExceptionCallback(callable $callback)
262
    {
263
        $this->exceptionCallback = $callback;
264
265
        return $this;
266
    }
267
268
    /**
269
     * @return callable
270
     */
271
    public function getExceptionCallback()
272
    {
273
        return $this->exceptionCallback;
274
    }
275
276
    /**
277
     * @return bool
278
     */
279
    public function hasExceptionCallback()
280
    {
281
        return null !== $this->exceptionCallback;
282
    }
283
284
    /**
285
     * @param FormObjectFactory $formObjectFactory
286
     */
287
    public function injectFormObjectFactory(FormObjectFactory $formObjectFactory)
288
    {
289
        $this->formObjectFactory = $formObjectFactory;
290
    }
291
}
292