Merge "[BUGFIX] ConfigurationManager use FrontendSimulator only if neccessary"
[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 * @api
36 */
37 class Tx_Extbase_MVC_Controller_Argument {
38
39 /**
40 * @var Tx_Extbase_Object_ObjectManagerInterface
41 */
42 protected $objectManager;
43
44 /**
45 * @var Tx_Extbase_Persistence_QueryFactory
46 */
47 protected $queryFactory;
48
49 /**
50 * @var Tx_Extbase_Configuration_ConfigurationManagerInterface
51 */
52 protected $configurationManager;
53
54 /**
55 * This is the old property mapper, which has been completely rewritten for 1.4.
56 * @var Tx_Extbase_Property_Mapper
57 */
58 protected $deprecatedPropertyMapper;
59
60 /**
61 * The new, completely rewritten property mapper since Extbase 1.4.
62 *
63 * @var Tx_Extbase_Property_PropertyMapper
64 */
65 protected $propertyMapper;
66
67 /**
68 * @var Tx_Extbase_Property_PropertyMappingConfigurationBuilder
69 */
70 protected $propertyMappingConfigurationBuilder;
71
72 /**
73 * @var Tx_Extbase_MVC_Controller_MvcPropertyMappingConfiguration
74 */
75 protected $propertyMappingConfiguration;
76
77 /**
78 * @var Tx_Extbase_Reflection_Service
79 */
80 protected $reflectionService;
81
82 /**
83 * @var Tx_Extbase_Service_TypeHandlingService
84 */
85 protected $typeHandlingService;
86
87 /**
88 * Name of this argument
89 * @var string
90 */
91 protected $name = '';
92
93 /**
94 * Short name of this argument
95 * @var string
96 */
97 protected $shortName = NULL;
98
99 /**
100 * Data type of this argument's value
101 * @var string
102 */
103 protected $dataType = NULL;
104
105 /**
106 * If the data type is an object, the class schema of the data type class is resolved
107 * @var Tx_Extbase_Reflection_ClassSchema
108 */
109 protected $dataTypeClassSchema;
110
111 /**
112 * TRUE if this argument is required
113 * @var boolean
114 */
115 protected $isRequired = FALSE;
116
117 /**
118 * Actual value of this argument
119 * @var object
120 */
121 protected $value = NULL;
122
123 /**
124 * Default value. Used if argument is optional.
125 * @var mixed
126 */
127 protected $defaultValue = NULL;
128
129 /**
130 * A custom validator, used supplementary to the base validation
131 * @var Tx_Extbase_Validation_Validator_ValidatorInterface
132 */
133 protected $validator = NULL;
134
135 /**
136 * The validation results. This can be asked if the argument has errors.
137 * @var Tx_Extbase_Error_Result
138 */
139 protected $validationResults = NULL;
140
141 /**
142 * Uid for the argument, if it has one
143 * @var string
144 */
145 protected $uid = NULL;
146
147 const ORIGIN_CLIENT = 0;
148 const ORIGIN_PERSISTENCE = 1;
149 const ORIGIN_PERSISTENCE_AND_MODIFIED = 2;
150 const ORIGIN_NEWLY_CREATED = 3;
151
152 /**
153 * The origin of the argument value. This is only meaningful after argument mapping.
154 *
155 * One of the ORIGIN_* constants above
156 * @var integer
157 */
158 protected $origin = 0;
159
160 /**
161 * Constructs this controller argument
162 *
163 * @param string $name Name of this argument
164 * @param string $dataType The data type of this argument
165 * @throws InvalidArgumentException if $name is not a string or empty
166 * @api
167 */
168 public function __construct($name, $dataType) {
169 if (!is_string($name)) throw new InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688);
170 if (strlen($name) === 0) throw new InvalidArgumentException('$name must be a non-empty string, ' . strlen($name) . ' characters given.', 1232551853);
171 $this->name = $name;
172 $this->dataType = $dataType;
173 }
174
175 /**
176 * Injects the object manager
177 *
178 * @param Tx_Extbase_Object_ObjectManagerInterface $objectManager
179 * @return void
180 */
181 public function injectObjectManager(Tx_Extbase_Object_ObjectManagerInterface $objectManager) {
182 $this->objectManager = $objectManager;
183 }
184
185 /**
186 * @param Tx_Extbase_Property_Mapper $deprecatedPropertyMapper
187 * @return void
188 */
189 public function injectDeprecatedPropertyMapper(Tx_Extbase_Property_Mapper $deprecatedPropertyMapper) {
190 $this->deprecatedPropertyMapper = $deprecatedPropertyMapper;
191 }
192
193 /**
194 * @param Tx_Extbase_Property_PropertyMapper $propertyMapper
195 * @return void
196 */
197 public function injectPropertyMapper(Tx_Extbase_Property_PropertyMapper $propertyMapper) {
198 $this->propertyMapper = $propertyMapper;
199 }
200
201 /**
202 * @param Tx_Extbase_Property_PropertyMappingConfigurationBuilder $propertyMappingConfigurationBuilder
203 * @return void
204 */
205 public function injectPropertyMappingConfigurationBuilder(Tx_Extbase_Property_PropertyMappingConfigurationBuilder $propertyMappingConfigurationBuilder) {
206 $this->propertyMappingConfigurationBuilder = $propertyMappingConfigurationBuilder;
207 }
208
209 /**
210 * @param Tx_Extbase_Reflection_Service $reflectionService
211 * @return void
212 */
213 public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
214 $this->reflectionService = $reflectionService;
215 $this->dataTypeClassSchema = (strpos($this->dataType, '_') !== FALSE) ? $this->reflectionService->getClassSchema($this->dataType) : NULL;
216 }
217
218 /**
219 * Injects the Persistence Manager
220 *
221 * @param Tx_Extbase_Persistence_ManagerInterface
222 * @return void
223 */
224 public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
225 $this->persistenceManager = $persistenceManager;
226 }
227
228 /**
229 * Injects a QueryFactory instance
230 *
231 * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
232 * @return void
233 */
234 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
235 $this->queryFactory = $queryFactory;
236 }
237
238 /**
239 * @param Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager
240 * @return void
241 */
242 public function injectConfigurationManager(Tx_Extbase_Configuration_ConfigurationManagerInterface $configurationManager) {
243 $this->configurationManager = $configurationManager;
244 }
245
246 /**
247 * Initializer.
248 *
249 * @return void
250 */
251 public function initializeObject() {
252 $this->propertyMappingConfiguration = $this->propertyMappingConfigurationBuilder->build('Tx_Extbase_MVC_Controller_MvcPropertyMappingConfiguration');
253 }
254
255 /**
256 * @param Tx_Extbase_Service_TypeHandlingService $typeHandlingService
257 * @return void
258 */
259 public function injectTypeHandlingService(Tx_Extbase_Service_TypeHandlingService $typeHandlingService) {
260 $this->typeHandlingService = $typeHandlingService;
261 $this->dataType = $this->typeHandlingService->normalizeType($this->dataType);
262 }
263
264 /**
265 * Returns the name of this argument
266 *
267 * @return string This argument's name
268 * @api
269 */
270 public function getName() {
271 return $this->name;
272 }
273
274 /**
275 * Sets the short name of this argument.
276 *
277 * @param string $shortName A "short name" - a single character
278 * @return Tx_Extbase_MVC_Controller_Argument $this
279 * @throws InvalidArgumentException if $shortName is not a character
280 * @api
281 */
282 public function setShortName($shortName) {
283 if ($shortName !== NULL && (!is_string($shortName) || strlen($shortName) !== 1)) throw new InvalidArgumentException('$shortName must be a single character or NULL', 1195824959);
284 $this->shortName = $shortName;
285 return $this;
286 }
287
288 /**
289 * Returns the short name of this argument
290 *
291 * @return string This argument's short name
292 * @api
293 */
294 public function getShortName() {
295 return $this->shortName;
296 }
297
298 /**
299 * Sets the data type of this argument's value
300 *
301 * @param string $dataType The data type. Can be either a built-in type such as "Text" or "Integer" or a fully qualified object name
302 * @return Tx_Extbase_MVC_Controller_Argument $this
303 * @api
304 */
305 public function setDataType($dataType) {
306 $this->dataType = $dataType;
307 $this->dataTypeClassSchema = $this->reflectionService->getClassSchema($dataType);
308 return $this;
309 }
310
311 /**
312 * Returns the data type of this argument's value
313 *
314 * @return string The data type
315 * @api
316 */
317 public function getDataType() {
318 return $this->dataType;
319 }
320
321 /**
322 * Marks this argument to be required
323 *
324 * @param boolean $required TRUE if this argument should be required
325 * @return Tx_Extbase_MVC_Controller_Argument $this
326 * @api
327 */
328 public function setRequired($required) {
329 $this->isRequired = (boolean)$required;
330 return $this;
331 }
332
333 /**
334 * Returns TRUE if this argument is required
335 *
336 * @return boolean TRUE if this argument is required
337 * @api
338 */
339 public function isRequired() {
340 return $this->isRequired;
341 }
342
343 /**
344 * Sets the default value of the argument
345 *
346 * @param mixed $defaultValue Default value
347 * @return Tx_Extbase_MVC_Controller_Argument $this
348 * @api
349 */
350 public function setDefaultValue($defaultValue) {
351 $this->defaultValue = $defaultValue;
352 return $this;
353 }
354
355 /**
356 * Returns the default value of this argument
357 *
358 * @return mixed The default value
359 * @api
360 */
361 public function getDefaultValue() {
362 return $this->defaultValue;
363 }
364
365 /**
366 * Sets a custom validator which is used supplementary to the base validation
367 *
368 * @param Tx_Extbase_Validation_Validator_ValidatorInterface $validator The actual validator object
369 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
370 * @api
371 */
372 public function setValidator(Tx_Extbase_Validation_Validator_ValidatorInterface $validator) {
373 $this->validator = $validator;
374 return $this;
375 }
376
377 /**
378 * Create and set a validator chain
379 *
380 * @param array Object names of the validators
381 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
382 * @api
383 * @deprecated since Extbase 1.4.0, will be removed in Extbase 1.6.0
384 */
385 public function setNewValidatorConjunction(array $objectNames) {
386 if ($this->validator === NULL) {
387 $this->validator = $this->objectManager->create('Tx_Extbase_Validation_Validator_ConjunctionValidator');
388 }
389 foreach ($objectNames as $objectName) {
390 if (!class_exists($objectName)) {
391 $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName;
392 }
393 $this->validator->addValidator($this->objectManager->get($objectName));
394 }
395 return $this;
396 }
397
398 /**
399 * Returns the set validator
400 *
401 * @return Tx_Extbase_Validation_Validator_ValidatorInterface The set validator, NULL if none was set
402 * @api
403 */
404 public function getValidator() {
405 return $this->validator;
406 }
407
408 /**
409 * Get the origin of the argument value. This is only meaningful after argument mapping.
410 *
411 * @return integer one of the ORIGIN_* constants
412 * @author Sebastian Kurfürst <sebastian@typo3.org>
413 * @deprecated since Extbase 1.4.0, will be removed in Extbase 1.6.0.
414 */
415 public function getOrigin() {
416 return $this->origin;
417 }
418
419 /**
420 * Sets the value of this argument.
421 *
422 * @param mixed $rawValue: The value of this argument
423 * @return Tx_Extbase_MVC_Controller_Argument $this
424 * @throws Tx_Extbase_MVC_Exception_InvalidArgumentValue if the argument is not a valid object of type $dataType
425 */
426 public function setValue($rawValue) {
427 if ($this->configurationManager->isFeatureEnabled('rewrittenPropertyMapper')) {
428 if ($rawValue === NULL) {
429 $this->value = NULL;
430 return $this;
431 }
432 if (is_object($rawValue) && $rawValue instanceof $this->dataType) {
433 $this->value = $rawValue;
434 return $this;
435 }
436 $this->value = $this->propertyMapper->convert($rawValue, $this->dataType, $this->propertyMappingConfiguration);
437 $this->validationResults = $this->propertyMapper->getMessages();
438 if ($this->validator !== NULL) {
439 // TODO: Validation API has also changed!!!
440 $validationMessages = $this->validator->validate($this->value);
441 $this->validationResults->merge($validationMessages);
442 }
443
444 return $this;
445 } else {
446 if ($rawValue === NULL || (is_object($rawValue) && $rawValue instanceof $this->dataType)) {
447 $this->value = $rawValue;
448 } else {
449 $this->value = $this->transformValue($rawValue);
450 }
451 return $this;
452 }
453 }
454
455 /**
456 * Checks if the value is a UUID or an array but should be an object, i.e.
457 * the argument's data type class schema is set. If that is the case, this
458 * method tries to look up the corresponding object instead.
459 *
460 * Additionally, it maps arrays to objects in case it is a normal object.
461 *
462 * @param mixed $value The value of an argument
463 * @return mixed
464 * @deprecated since Extbase 1.4, will be removed in Extbase 1.6.
465 */
466 protected function transformValue($value) {
467 if (!class_exists($this->dataType)) {
468 return $value;
469 }
470 $transformedValue = NULL;
471 if ($this->dataTypeClassSchema !== NULL) {
472 // The target object is an Entity or ValueObject.
473 if (is_numeric($value)) {
474 $this->origin = self::ORIGIN_PERSISTENCE;
475 $transformedValue = $this->findObjectByUid($value);
476 } elseif (is_array($value)) {
477 $this->origin = self::ORIGIN_PERSISTENCE_AND_MODIFIED;
478 $transformedValue = $this->deprecatedPropertyMapper->map(array_keys($value), $value, $this->dataType);
479 }
480 } else {
481 if (!is_array($value)) {
482 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);
483 }
484 $this->origin = self::ORIGIN_NEWLY_CREATED;
485 $transformedValue = $this->deprecatedPropertyMapper->map(array_keys($value), $value, $this->dataType);
486 }
487
488 if (!($transformedValue instanceof $this->dataType) && ($transformedValue !== NULL || $this->isRequired())) {
489 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue(
490 'The value must be of type "' . $this->dataType . '", but was of type "'
491 . (is_object($transformedValue) ? get_class($transformedValue) : gettype($transformedValue)) . '".'
492 // add mappingResult errors to exception
493 . ($this->deprecatedPropertyMapper->getMappingResults()->hasErrors()
494 ? '<p>' . implode('<br />', $this->deprecatedPropertyMapper->getMappingResults()->getErrors()) . '</p>'
495 : ''),
496 1251730701);
497 }
498 return $transformedValue;
499 }
500
501 /**
502 * Finds an object from the repository by searching for its technical UID.
503 *
504 * @param int $uid The object's uid
505 * @return object Either the object matching the uid or, if none or more than one object was found, NULL
506 */
507 protected function findObjectByUid($uid) {
508 $query = $this->queryFactory->create($this->dataType);
509 $query->getQuerySettings()->setRespectSysLanguage(FALSE);
510 $query->getQuerySettings()->setRespectStoragePage(FALSE);
511 return $query->matching(
512 $query->equals('uid', $uid))
513 ->execute()
514 ->getFirst();
515 }
516
517 /**
518 * Returns the value of this argument
519 *
520 * @return object The value of this argument - if none was set, NULL is returned
521 * @api
522 */
523 public function getValue() {
524 if ($this->value === NULL) {
525 return $this->defaultValue;
526 } else {
527 return $this->value;
528 }
529 }
530
531 /**
532 * Checks if this argument has a value set.
533 *
534 * @return boolean TRUE if a value was set, otherwise FALSE
535 * @deprecated since Extbase 1.4.0, will be removed with Extbase 1.6.0
536 */
537 public function isValue() {
538 return $this->value !== NULL;
539 }
540
541 /**
542 * Return the Property Mapping Configuration used for this argument; can be used by the initialize*action to modify the Property Mapping.
543 *
544 * @return Tx_Extbase_MVC_Controller_MvcPropertyMappingConfiguration
545 * @author Sebastian Kurfürst <sebastian@typo3.org>
546 * @api
547 */
548 public function getPropertyMappingConfiguration() {
549 return $this->propertyMappingConfiguration;
550 }
551
552 /**
553 * @return boolean TRUE if the argument is valid, FALSE otherwise
554 * @author Sebastian Kurfürst <sebastian@typo3.org>
555 * @api
556 */
557 public function isValid() {
558 return !$this->validationResults->hasErrors();
559 }
560
561 /**
562 * @return array<Tx_Extbase_Error_Result> Validation errors which have occured.
563 * @author Sebastian Kurfürst <sebastian@typo3.org>
564 * @api
565 */
566 public function getValidationResults() {
567 return $this->validationResults;
568 }
569
570 /**
571 * Returns a string representation of this argument's value
572 *
573 * @return string
574 * @api
575 */
576 public function __toString() {
577 return (string)$this->value;
578 }
579 }
580 ?>