[~TASK] Extbase: Refactored Dispatcher. Moved initialization of cache and reflection...
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / MVC / Controller / Argument.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This class is a backport of the corresponding class of FLOW3.
9 * All credits go to the v5 team.
10 *
11 * This script is part of the TYPO3 project. The TYPO3 project is
12 * free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * A controller argument
30 *
31 * @package Extbase
32 * @subpackage MVC\Controller
33 * @version $ID:$
34 * @scope prototype
35 */
36 class Tx_Extbase_MVC_Controller_Argument {
37
38 /**
39 * @var Tx_Extbase_Persistence_QueryFactory
40 */
41 protected $queryFactory;
42
43 /**
44 * @var Tx_Extbase_Property_Mapper
45 */
46 protected $propertyMapper;
47
48 /**
49 * @var Tx_Extbase_Reflection_Service
50 */
51 protected $reflectionService;
52
53 /**
54 * Name of this argument
55 * @var string
56 */
57 protected $name = '';
58
59 /**
60 * Short name of this argument
61 * @var string
62 */
63 protected $shortName = NULL;
64
65 /**
66 * Data type of this argument's value
67 * @var string
68 */
69 protected $dataType = NULL;
70
71 /**
72 * If the data type is an object, the class schema of the data type class is resolved
73 * @var Tx_Extbase_Reflection_ClassSchema
74 */
75 protected $dataTypeClassSchema;
76
77 /**
78 * TRUE if this argument is required
79 * @var boolean
80 */
81 protected $isRequired = FALSE;
82
83 /**
84 * Actual value of this argument
85 * @var object
86 */
87 protected $value = NULL;
88
89 /**
90 * Default value. Used if argument is optional.
91 * @var mixed
92 */
93 protected $defaultValue = NULL;
94
95 /**
96 * A custom validator, used supplementary to the base validation
97 * @var Tx_Extbase_Validation_Validator_ValidatorInterface
98 */
99 protected $validator = NULL;
100
101 /**
102 * Uid for the argument, if it has one
103 * @var string
104 */
105 protected $uid = NULL;
106
107 const ORIGIN_CLIENT = 0;
108 const ORIGIN_PERSISTENCE = 1;
109 const ORIGIN_PERSISTENCE_AND_MODIFIED = 2;
110 const ORIGIN_NEWLY_CREATED = 3;
111
112 /**
113 * The origin of the argument value. This is only meaningful after argument mapping.
114 *
115 * One of the ORIGIN_* constants above
116 * @var integer
117 */
118 protected $origin = 0;
119
120 /**
121 * Constructs this controller argument
122 *
123 * @param string $name Name of this argument
124 * @param string $dataType The data type of this argument
125 * @throws InvalidArgumentException if $name is not a string or empty
126 * @api
127 */
128 public function __construct($name, $dataType) {
129 if (!is_string($name)) throw new InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688);
130 if (strlen($name) === 0) throw new InvalidArgumentException('$name must be a non-empty string, ' . strlen($name) . ' characters given.', 1232551853);
131 $this->name = $name;
132 $this->dataType = $dataType;
133 }
134
135 /**
136 * Initializes this object
137 *
138 * @return void
139 */
140 public function initializeObject() {
141 $this->reflectionService = t3lib_div::makeInstance('Tx_Extbase_Reflection_Service');
142 $this->propertyMapper = t3lib_div::makeInstance('Tx_Extbase_Property_Mapper');
143 $this->propertyMapper->injectReflectionService($this->reflectionService);
144 $this->dataTypeClassSchema = $this->reflectionService->getClassSchema($this->dataType);
145 }
146
147 /**
148 * Injects the Persistence Manager
149 *
150 * @param Tx_Extbase_Persistence_ManagerInterface
151 * @return void
152 */
153 public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
154 $this->persistenceManager = $persistenceManager;
155 }
156
157 /**
158 * Injects a QueryFactory instance
159 *
160 * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
161 * @return void
162 */
163 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
164 $this->queryFactory = $queryFactory;
165 }
166
167 /**
168 * Returns the name of this argument
169 *
170 * @return string This argument's name
171 * @api
172 */
173 public function getName() {
174 return $this->name;
175 }
176
177 /**
178 * Sets the short name of this argument.
179 *
180 * @param string $shortName A "short name" - a single character
181 * @return Tx_Extbase_MVC_Controller_Argument $this
182 * @throws InvalidArgumentException if $shortName is not a character
183 * @api
184 */
185 public function setShortName($shortName) {
186 if ($shortName !== NULL && (!is_string($shortName) || strlen($shortName) !== 1)) throw new InvalidArgumentException('$shortName must be a single character or NULL', 1195824959);
187 $this->shortName = $shortName;
188 return $this;
189 }
190
191 /**
192 * Returns the short name of this argument
193 *
194 * @return string This argument's short name
195 * @api
196 */
197 public function getShortName() {
198 return $this->shortName;
199 }
200
201 /**
202 * Sets the data type of this argument's value
203 *
204 * @param string $dataType The data type. Can be either a built-in type such as "Text" or "Integer" or a fully qualified object name
205 * @return Tx_Extbase_MVC_Controller_Argument $this
206 * @api
207 */
208 public function setDataType($dataType) {
209 $this->dataType = $dataType;
210 return $this;
211 }
212
213 /**
214 * Returns the data type of this argument's value
215 *
216 * @return string The data type
217 * @api
218 */
219 public function getDataType() {
220 return $this->dataType;
221 }
222
223 /**
224 * Marks this argument to be required
225 *
226 * @param boolean $required TRUE if this argument should be required
227 * @return Tx_Extbase_MVC_Controller_Argument $this
228 * @api
229 */
230 public function setRequired($required) {
231 $this->isRequired = (boolean)$required;
232 return $this;
233 }
234
235 /**
236 * Returns TRUE if this argument is required
237 *
238 * @return boolean TRUE if this argument is required
239 * @api
240 */
241 public function isRequired() {
242 return $this->isRequired;
243 }
244
245 /**
246 * Sets the default value of the argument
247 *
248 * @param mixed $defaultValue Default value
249 * @return void
250 * @api
251 */
252 public function setDefaultValue($defaultValue) {
253 $this->defaultValue = $defaultValue;
254 }
255
256 /**
257 * Returns the default value of this argument
258 *
259 * @return mixed The default value
260 * @api
261 */
262 public function getDefaultValue() {
263 return $this->defaultValue;
264 }
265
266 /**
267 * Sets a custom validator which is used supplementary to the base validation
268 *
269 * @param Tx_Extbase_Validation_Validator_ValidatorInterface $validator The actual validator object
270 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
271 * @api
272 */
273 public function setValidator(Tx_Extbase_Validation_Validator_ValidatorInterface $validator) {
274 $this->validator = $validator;
275 return $this;
276 }
277
278 /**
279 * Create and set a validator chain
280 *
281 * @param array Object names of the validators
282 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
283 * @api
284 */
285 public function setNewValidatorConjunction(array $objectNames) {
286 if ($this->validator === NULL) {
287 $this->validator = t3lib_div::makeInstance('Tx_Extbase_Validation_Validator_ConjunctionValidator');
288 }
289 foreach ($objectNames as $objectName) {
290 if (!class_exists($objectName)) $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName;
291 $this->validator->addValidator(t3lib_div::makeInstance($objectName));
292 }
293 return $this;
294 }
295
296 /**
297 * Returns the set validator
298 *
299 * @return Tx_Extbase_Validation_Validator_ValidatorInterface The set validator, NULL if none was set
300 * @api
301 */
302 public function getValidator() {
303 return $this->validator;
304 }
305
306 /**
307 * Get the origin of the argument value. This is only meaningful after argument mapping.
308 *
309 * @return integer one of the ORIGIN_* constants
310 * @author Sebastian Kurf├╝rst <sebastian@typo3.org>
311 */
312 public function getOrigin() {
313 return $this->origin;
314 }
315
316 /**
317 * Sets the value of this argument.
318 *
319 * @param mixed $value: The value of this argument
320 * @return Tx_Extbase_MVC_Controller_Argument $this
321 * @throws Tx_Extbase_MVC_Exception_InvalidArgumentValue if the argument is not a valid object of type $dataType
322 */
323 public function setValue($value) {
324 $this->value = $this->transformValue($value);
325
326 return $this;
327 }
328
329 /**
330 * Checks if the value is a UUID or an array but should be an object, i.e.
331 * the argument's data type class schema is set. If that is the case, this
332 * method tries to look up the corresponding object instead.
333 *
334 * Additionally, it maps arrays to objects in case it is a normal object.
335 *
336 * @param mixed $value The value of an argument
337 * @return mixed
338 */
339 protected function transformValue($value) {
340 if ($value === NULL) {
341 return NULL;
342 }
343 if (!class_exists($this->dataType)) {
344 return $value;
345 }
346 $transformedValue = NULL;
347 if ($this->dataTypeClassSchema !== NULL) {
348 // The target object is an Entity or ValueObject.
349 if (is_numeric($value)) {
350 $this->origin = self::ORIGIN_PERSISTENCE;
351 $transformedValue = $this->findObjectByUid($value);
352 } elseif (is_array($value)) {
353 $this->origin = self::ORIGIN_PERSISTENCE_AND_MODIFIED;
354 $transformedValue = $this->propertyMapper->map(array_keys($value), $value, $this->dataType);
355 }
356 } else {
357 if (!is_array($value)) {
358 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('The value was a simple type, so we could not map it to an object. Maybe the @entity or @valueobject annotations are missing?', 1251730701);
359 }
360 $this->origin = self::ORIGIN_NEWLY_CREATED;
361 $transformedValue = $this->propertyMapper->map(array_keys($value), $value, $this->dataType);
362 }
363
364 if (!($transformedValue instanceof $this->dataType)) {
365 if (is_object($transformedValue)) {
366 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('The value must be of type "' . $this->dataType . '", but was of type "' . get_class($transformedValue) . '".', 1251730701);
367 } else {
368 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('The value must be of type "' . $this->dataType . '", but was of type "' . gettype($transformedValue) . '".', 1251730702);
369 }
370 }
371 return $transformedValue;
372 }
373
374 /**
375 * Finds an object from the repository by searching for its technical UID.
376 *
377 * @param int $uid The object's uid
378 * @return mixed Either the object matching the uid or, if none or more than one object was found, FALSE
379 */
380 protected function findObjectByUid($uid) {
381 $query = $this->queryFactory->create($this->dataType);
382 $result = $query->matching($query->withUid($uid))->execute();
383 $object = NULL;
384 if (count($result) > 0) {
385 $object = current($result);
386 }
387 return $object;
388 }
389
390 /**
391 * Returns the value of this argument
392 *
393 * @return object The value of this argument - if none was set, NULL is returned
394 * @api
395 */
396 public function getValue() {
397 if ($this->value === NULL) {
398 return $this->defaultValue;
399 } else {
400 return $this->value;
401 }
402 }
403
404 /**
405 * Checks if this argument has a value set.
406 *
407 * @return boolean TRUE if a value was set, otherwise FALSE
408 */
409 public function isValue() {
410 return $this->value !== NULL;
411 }
412
413 /**
414 * Returns a string representation of this argument's value
415 *
416 * @return string
417 * @api
418 */
419 public function __toString() {
420 return (string)$this->value;
421 }
422 }
423 ?>