Extbase:
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / MVC / Controller / ActionController.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * A multi action controller
27 *
28 * @package TYPO3
29 * @subpackage extbase
30 * @version $ID:$
31 */
32 class Tx_Extbase_MVC_Controller_ActionController extends Tx_Extbase_MVC_Controller_AbstractController {
33
34 /**
35 * @var Tx_Extbase_Reflection_Service
36 */
37 protected $reflectionService;
38
39 /**
40 * @var boolean If initializeView() should be called on an action invocation.
41 */
42 protected $initializeView = TRUE;
43
44 /**
45 * By default a view with the same name as the current action is provided. Contains NULL if none was found.
46 * @var Tx_Extbase_MVC_View_AbstractView
47 */
48 protected $view = NULL;
49
50 /**
51 * By default $this->viewObjectNamePattern is used to find a matching view object.
52 * If no custom view class can be found, $this->defaultViewObjectName will be used.
53 * @var string
54 */
55 protected $standardViewObjectName = 'Tx_Fluid_View_TemplateView';
56
57 /**
58 * Pattern after which the view object name is built
59 *
60 * @var string
61 */
62 // SK: Decision: Do we support "format"?
63 protected $viewObjectNamePattern = 'Tx_@extension_View_@controller_@action';
64
65 /**
66 * Name of the action method
67 * @var string
68 */
69 protected $actionMethodName = 'indexAction';
70
71 /**
72 * Name of the special error action method which is called in case of errors
73 * @var string
74 */
75 protected $errorMethodName = 'errorAction';
76
77 /**
78 * Injects the reflection service
79 *
80 * @param Tx_Extbase_Reflection_Service $reflectionService
81 * @return void
82 * @internal
83 */
84 public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
85 $this->reflectionService = $reflectionService;
86 }
87
88 /**
89 * Checks if the current request type is supported by the controller.
90 *
91 * If your controller only supports certain request types, either
92 * replace / modify the supporteRequestTypes property or override this
93 * method.
94 *
95 * @param Tx_Extbase_MVC_Request $request The current request
96 * @return boolean TRUE if this request type is supported, otherwise FALSE
97 */
98 public function canProcessRequest(Tx_Extbase_MVC_Request $request) {
99 return parent::canProcessRequest($request);
100
101 }
102
103 /**
104 * Handles a request. The result output is returned by altering the given response.
105 *
106 * @param Tx_Extbase_MVC_Request $request The request object
107 * @param Tx_Extbase_MVC_Response $response The response, modified by this handler
108 * @return void
109 */
110 public function processRequest(Tx_Extbase_MVC_Request $request, Tx_Extbase_MVC_Response $response) {
111 if (!$this->canProcessRequest($request)) throw new Tx_Extbase_MVC_Exception_UnsupportedRequestType(get_class($this) . ' does not support requests of type "' . get_class($request) . '". Supported types are: ' . implode(' ', $this->supportedRequestTypes) , 1187701131);
112
113 $this->request = $request;
114 $this->request->setDispatched(TRUE);
115 $this->response = $response;
116
117 $this->actionMethodName = $this->resolveActionMethodName();
118 if ($this->initializeView) $this->initializeView();
119
120 $this->initializeActionMethodArguments();
121 $this->initializeControllerArgumentsBaseValidators();
122 $this->initializeActionMethodValidators();
123
124 $this->initializeAction();
125 $actionInitializationMethodName = 'initialize' . ucfirst($this->actionMethodName);
126 if (method_exists($this, $actionInitializationMethodName)) {
127 call_user_func(array($this, $actionInitializationMethodName));
128 }
129
130 $this->mapRequestArgumentsToControllerArguments();
131 $this->callActionMethod();
132 }
133
134 /**
135 * Implementation of the arguments initilization in the action controller:
136 * Automatically registers arguments of the current action
137 *
138 * Don't override this method - use initializeArguments() instead.
139 *
140 * @return void
141 * @see initializeArguments()
142 * @internal
143 */
144 protected function initializeActionMethodArguments() {
145 $methodParameters = $this->reflectionService->getMethodParameters(get_class($this), $this->actionMethodName);
146 foreach ($methodParameters as $parameterName => $parameterInfo) {
147 $dataType = 'Text';
148 if (isset($parameterInfo['type'])) {
149 $dataType = $parameterInfo['type'];
150 } elseif ($parameterInfo['array']) {
151 $dataType = 'array';
152 }
153 $defaultValue = (isset($parameterInfo['defaultValue']) ? $parameterInfo['defaultValue'] : NULL);
154
155 $this->arguments->addNewArgument($parameterName, $dataType, ($parameterInfo['optional'] === FALSE), $defaultValue);
156 }
157 }
158
159 /**
160 * Detects and registers any additional validators for arguments which were
161 * specified in the @validate annotations of an action method
162 *
163 * @return void
164 * @internal
165 */
166 protected function initializeActionMethodValidators() {
167 $validatorChains = $this->validatorResolver->buildMethodArgumentsValidatorChains(get_class($this), $this->actionMethodName);
168 foreach ($validatorChains as $argumentName => $validatorChain) {
169 if (!isset($this->arguments[$argumentName])) throw new Tx_Extbase_MVC_Exception_NoSuchArgument('Found custom validation rule for non existing argument "' . $argumentName . '" in ' . get_class($this) . '->' . $this->actionMethodName . '().', 1239853108);
170 $this->arguments[$argumentName]->setValidator($validatorChain);
171 }
172 }
173
174 /**
175 * Determines the action method and assures that the method exists.
176 *
177 * @return string The action method name
178 * @throws Tx_Extbase_Exception_NoSuchAction if the action specified in the request object does not exist (and if there's no default action either).
179 */
180 protected function resolveActionMethodName() {
181 $actionMethodName = $this->request->getControllerActionName() . 'Action';
182 if (!method_exists($this, $actionMethodName)) throw new Tx_Extbase_Exception_NoSuchAction('An action "' . $actionMethodName . '" does not exist in controller "' . get_class($this) . '".', 1186669086);
183 return $actionMethodName;
184 }
185
186 /**
187 * Calls the specified action method and passes the arguments.
188 *
189 * If the action returns a string, it is appended to the content in the
190 * response object. If the action doesn't return anything and a valid
191 * view exists, the view is rendered automatically.
192 *
193 * @param string $actionMethodName Name of the action method to call
194 * @return void
195 * @internal
196 */
197 protected function callActionMethod() {
198 $argumentsAreValid = TRUE;
199 $preparedArguments = array();
200 foreach ($this->arguments as $argument) {
201 $this->preProcessArgument($argument);
202 $preparedArguments[] = $argument->getValue();
203 }
204
205 if ($this->argumentsMappingResults->hasErrors()) {
206 $actionResult = call_user_func(array($this, $this->errorMethodName));
207 } else {
208 $actionResult = call_user_func_array(array($this, $this->actionMethodName), $preparedArguments);
209 }
210 if ($actionResult === NULL && $this->view instanceof Tx_Extbase_MVC_View_ViewInterface) {
211 $this->response->appendContent($this->view->render());
212 } elseif (is_string($actionResult) && strlen($actionResult) > 0) {
213 $this->response->appendContent($actionResult);
214 }
215 }
216
217 /**
218 * This is a template method to process unvalid arguments. Overwrite this method in your concrete controller.
219 *
220 * @param Tx_Extbase_MVC_Controller_Argument $argument The argument
221 * @return void
222 */
223 protected function preProcessArgument(Tx_Extbase_MVC_Controller_Argument $argument) {
224 }
225
226 /**
227 * Prepares a view for the current action and stores it in $this->view.
228 * By default, this method tries to locate a view with a name matching
229 * the current action.
230 *
231 * @return void
232 */
233 protected function initializeView() {
234 $this->view = t3lib_div::makeInstance($this->resolveViewObjectName());
235 $this->view->setRequest($this->request);
236 }
237
238 /**
239 * Determines the fully qualified view object name.
240 *
241 * @return string The fully qualified view object name
242 */
243 protected function resolveViewObjectName() {
244 $viewObjectName = str_replace('@extension', $this->request->getControllerExtensionName(), $this->viewObjectNamePattern);
245 $viewObjectName = str_replace('@controller', $this->request->getControllerName(), $viewObjectName);
246 $viewObjectName = str_replace('@action', ucfirst($this->request->getControllerActionName()), $viewObjectName);
247 if (!class_exists($viewObjectName)) {
248 if (class_exists($this->standardViewObjectName)) {
249 $viewObjectName = $this->standardViewObjectName;
250 } else {
251 $viewObjectName = 'Tx_Extbase_View_EmptyView';
252 }
253 }
254 return $viewObjectName;
255 }
256
257 /**
258 * Initializes the controller before invoking an action method.
259 *
260 * Override this method to solve tasks which all actions have in
261 * common.
262 *
263 * @return void
264 */
265 protected function initializeAction() {
266 }
267
268 /**
269 * A special action which is called if the originally intended action could
270 * not be called, for example if the arguments were not valid.
271 *
272 * @return string
273 */
274 protected function errorAction() {
275 $message = 'An error occurred while trying to call ' . get_class($this) . '->' . $this->actionMethodName . '().' . PHP_EOL;
276 foreach ($this->argumentsMappingResults->getErrors() as $error) {
277 $message .= 'Error: ' . $error . PHP_EOL;
278 }
279 foreach ($this->argumentsMappingResults->getWarnings() as $warning) {
280 $message .= 'Warning: ' . $warning . PHP_EOL;
281 }
282 return $message;
283 }
284 }
285 ?>