Completed
Push — wip/steps ( 0797ca...b52cc8 )
by Romain
02:44
created

FormController::processFormAction()   A

Complexity

Conditions 5
Paths 26

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 26
nop 0
dl 0
loc 22
rs 9.2568
c 0
b 0
f 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;
15
16
use Romm\Formz\Controller\Processor\ControllerProcessor;
17
use Romm\Formz\Core\Core;
18
use Romm\Formz\Form\FormObject\FormObject;
19
use Romm\Formz\Form\FormObject\FormObjectFactory;
20
use Romm\Formz\Middleware\Processor\MiddlewareProcessor;
21
use Romm\Formz\Middleware\Request\Exception\ForwardException;
22
use Romm\Formz\Middleware\Request\Exception\RedirectException;
23
use Romm\Formz\Middleware\Request\Exception\StopPropagationException;
24
use Throwable;
25
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
26
use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException;
27
28
/**
29
 * This is the main form controller, which is called before a controller action
30
 * with at least form argument is called.
31
 *
32
 * It will process the argument form(s), for instance by calling all its
33
 * middlewares. It allows manipulating the request, and information about the
34
 * form, before the actual action is called.
35
 */
36
class FormController extends ActionController
37
{
38
    /**
39
     * @var ControllerProcessor
40
     */
41
    protected $processor;
42
43
    /**
44
     * Main action used to dispatch the request properly, depending on FormZ
45
     * configuration.
46
     *
47
     * The request is based on the previously called controller action, and is
48
     * used to list which forms are handled (every argument of the action method
49
     * that implements the interface `FormInterface`).
50
     *
51
     * Middlewares will be called for each form argument, and may modify the
52
     * request, which is then dispatched again with modified data.
53
     *
54
     * @throws Throwable
55
     */
56
    public function processFormAction()
57
    {
58
        try {
59
            $this->invokeMiddlewares();
60
            $this->manageRequestResult();
61
        } catch (Throwable $exception) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
62
            if ($exception instanceof StopPropagationException) {
63
                if ($exception instanceof RedirectException) {
64
                    $this->redirectFromException($exception);
65
                } elseif (false === $exception instanceof ForwardException) {
66
                    $this->resetSubstepsLevel();
67
                    $this->forwardToReferrer();
68
                }
69
            } else {
70
                $this->callExceptionHandler($exception);
71
            }
72
        } finally {
73
            $this->persistForms();
74
        }
75
76
        $this->continueRequest();
77
    }
78
79
    /**
80
     * Wrapping the rendering with a try/catch to handle the exception callback
81
     * if there is one.
82
     */
83
    protected function callActionMethod()
84
    {
85
        try {
86
            parent::callActionMethod();
87
        } catch (Throwable $exception) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
88
            $this->callExceptionHandler($exception);
89
        }
90
    }
91
92
    /**
93
     * @param FormObject $formObject
94
     */
95
    public function formObjectErrorAction(FormObject $formObject)
96
    {
97
        $this->view->assign('formObject', $formObject);
98
    }
99
100
    /**
101
     * @param Throwable $exception
102
     * @throws Throwable
103
     */
104
    protected function callExceptionHandler(Throwable $exception)
105
    {
106
        if ($exception instanceof StopActionException
107
            || !$this->processor->hasExceptionCallback()
108
        ) {
109
            throw $exception;
110
        }
111
112
        call_user_func($this->processor->getExceptionCallback(), $exception);
113
    }
114
115
    /**
116
     * Will fetch every form argument for this request, and dispatch every
117
     * middleware that was registered in its TypoScript configuration.
118
     */
119
    protected function invokeMiddlewares()
120
    {
121
        foreach ($this->processor->getRequestForms() as $formObject) {
122
            /** @var MiddlewareProcessor $middlewareProcessor */
123
            $middlewareProcessor = Core::instantiate(MiddlewareProcessor::class, $formObject, $this->processor);
124
125
            $middlewareProcessor->run();
126
        }
127
    }
128
129
    /**
130
     * @todo
131
     */
132
    protected function resetSubstepsLevel()
133
    {
134
        foreach ($this->processor->getRequestForms() as $formObject) {
135
            $stepService = FormObjectFactory::get()->getStepService($formObject);
136
            $stepService->setSubstepsLevel(1);
137
        }
138
    }
139
140
    /**
141
     * Will check if the request result contains error; if errors are found, the
142
     * request is forwarded to the referring request, with the arguments of the
143
     * current request.
144
     */
145
    protected function manageRequestResult()
146
    {
147
        $result = $this->processor->getRequest()->getOriginalRequestMappingResults();
148
        $this->request->setOriginalRequestMappingResults($result);
149
150
        if ($result->hasErrors()) {
151
            $this->forwardToReferrer();
152
        }
153
    }
154
155
    /**
156
     * Loops on every form of this request, and persists each one.
157
     */
158
    protected function persistForms()
159
    {
160
        foreach ($this->processor->getRequestForms() as $formObject) {
161
            if ($formObject->hasForm()
162
                && ($formObject->isPersistent()
163
                    || $formObject->formWasSubmitted()
164
                )
165
            ) {
166
                $formObject->getPersistenceManager()->save();
167
168
                if ($formObject->isPersistent()) {
169
                    $formObject->getFormMetadata()->persist();
170
                }
171
            }
172
        }
173
    }
174
175
    /**
176
     * Forwards the request to the original request that led to this controller.
177
     *
178
     * @throws StopActionException
179
     */
180
    protected function continueRequest()
181
    {
182
        $this->request->setDispatched(false);
183
        $request = $this->processor->getRequest();
184
185
        $this->request->setPluginName($request->getPluginName());
186
        $this->request->setControllerVendorName($request->getControllerVendorName());
187
        $this->request->setControllerExtensionName($request->getControllerExtensionName());
188
        $this->request->setControllerName($request->getControllerName());
189
        $this->request->setControllerActionName($request->getControllerActionName());
190
        $this->request->setArguments($this->processor->getRequest()->getArguments());
191
192
        throw new StopActionException;
193
    }
194
195
    /**
196
     * Forwards to the referrer request. It will also fill the arguments of the
197
     * action with the ones from the source request.
198
     *
199
     * @throws StopActionException
200
     */
201
    protected function forwardToReferrer()
202
    {
203
        /*
204
         * If the original request is filled, a forward to referrer has already
205
         * been done.
206
         */
207
        if ($this->request->getOriginalRequest()) {
208
            return;
209
        }
210
211
        $referringRequest = $this->processor->getRequest()->getReferringRequest();
212
213
        if ($referringRequest) {
214
            $originalRequest = clone $this->request;
215
            $this->request->setDispatched(false);
216
217
            $this->request->setControllerVendorName($referringRequest->getControllerVendorName());
218
            $this->request->setControllerVendorName($referringRequest->getControllerVendorName());
219
            $this->request->setControllerExtensionName($referringRequest->getControllerExtensionName());
220
            $this->request->setControllerName($referringRequest->getControllerName());
221
            $this->request->setControllerActionName($referringRequest->getControllerActionName());
222
            $this->request->setArguments($this->processor->getRequest()->getArguments());
223
            $this->request->setOriginalRequest($originalRequest);
224
225
            throw new StopActionException;
226
        } else {
227
            /**
228
             * @todo ?
229
             * @see \TYPO3\CMS\Extbase\Mvc\Controller\ActionController::forwardToReferringRequest()
230
             */
231
        }
232
    }
233
234
    /**
235
     * @param RedirectException $redirectException
236
     */
237
    protected function redirectFromException(RedirectException $redirectException)
238
    {
239
        $this->uriBuilder->setRequest($this->processor->getRequest());
240
241
        $this->redirect(
242
            $redirectException->getActionName(),
243
            $redirectException->getControllerName(),
244
            $redirectException->getExtensionName(),
245
            $redirectException->getArguments(),
246
            $redirectException->getPageUid(),
247
            $redirectException->getDelay(),
248
            $redirectException->getStatusCode()
249
        );
250
    }
251
252
    /**
253
     * @param ControllerProcessor $processor
254
     */
255
    public function injectProcessor(ControllerProcessor $processor)
256
    {
257
        $this->processor = $processor;
258
    }
259
}
260