Completed
Push — feature/middleware ( 180b74...67e214 )
by Romain
04:26
created

ControllerProcessor::hasExceptionCallback()   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 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
     * Contains information about the last request that was dispatched to FormZ.
80
     * It is used to prevent infinite loop.
81
     *
82
     * @var string
83
     */
84
    protected $lastDispatchedRequest;
85
86
    /**
87
     * @var callable
88
     */
89
    protected $exceptionCallback;
90
91
    /**
92
     * @param MvcRequest $request
93
     * @param Arguments  $requestArguments
94
     * @param string     $scope
95
     * @return $this
96
     */
97
    public static function prepare(MvcRequest $request, Arguments $requestArguments, $scope)
98
    {
99
        return self::get()->setData($request, $requestArguments, $scope, []);
0 ignored issues
show
Bug introduced by
It seems like setData() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
100
    }
101
102
    /**
103
     * Injects the data needed for this class to work properly. This method must
104
     * be called before the `dispatch` method 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 picks 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 (class_exists($type)
205
                    && in_array(FormInterface::class, class_implements($type))
206
                ) {
207
                    $formClassName = $argument->getDataType();
208
                    $formName = $argument->getName();
209
                    $formObject = $this->formObjectFactory->getInstanceWithClassName($formClassName, $formName);
210
211
                    $this->formArguments[$formName] = $formObject;
212
                }
213
            }
214
        }
215
216
        return $this->formArguments;
217
    }
218
219
    /**
220
     * @return Request
221
     */
222
    public function getRequest()
223
    {
224
        return $this->request;
225
    }
226
227
    /**
228
     * @return Arguments
229
     */
230
    public function getRequestArguments()
231
    {
232
        return $this->requestArguments;
233
    }
234
235
    /**
236
     * @return array
237
     */
238
    public function getSettings()
239
    {
240
        return $this->settings;
241
    }
242
243
    /**
244
     * @return string
245
     */
246
    public function getScope()
247
    {
248
        return $this->scope;
249
    }
250
251
    /**
252
     * @param callable $callback
253
     * @return $this
254
     */
255
    public function setExceptionCallback(callable $callback)
256
    {
257
        $this->exceptionCallback = $callback;
258
259
        return $this;
260
    }
261
262
    /**
263
     * @return callable
264
     */
265
    public function getExceptionCallback()
266
    {
267
        return $this->exceptionCallback;
268
    }
269
270
    /**
271
     * @return bool
272
     */
273
    public function hasExceptionCallback()
274
    {
275
        return null !== $this->exceptionCallback;
276
    }
277
278
    /**
279
     * @param FormObjectFactory $formObjectFactory
280
     */
281
    public function injectFormObjectFactory(FormObjectFactory $formObjectFactory)
282
    {
283
        $this->formObjectFactory = $formObjectFactory;
284
    }
285
}
286