75c616b3c9d80210d5a371d806ac1c961b6a33e8
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Mvc / Controller / Argument.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Mvc\Controller;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16 /**
17 * A controller argument
18 *
19 * @api
20 */
21 class Argument {
22
23 /**
24 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
25 * @inject
26 */
27 protected $objectManager;
28
29 /**
30 * @var \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory
31 * @inject
32 */
33 protected $queryFactory;
34
35 /**
36 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
37 * @inject
38 */
39 protected $configurationManager;
40
41 /**
42 * This is the old property mapper, which has been completely rewritten for 1.4.
43 * @inject
44 *
45 * @var \TYPO3\CMS\Extbase\Property\Mapper
46 */
47 protected $deprecatedPropertyMapper;
48
49 /**
50 * The new, completely rewritten property mapper since Extbase 1.4.
51 *
52 * @var \TYPO3\CMS\Extbase\Property\PropertyMapper
53 * @inject
54 */
55 protected $propertyMapper;
56
57 /**
58 * @var \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration
59 * @inject
60 */
61 protected $propertyMappingConfiguration;
62
63 /**
64 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
65 */
66 protected $reflectionService;
67
68 /**
69 * @var \TYPO3\CMS\Extbase\Service\TypeHandlingService
70 */
71 protected $typeHandlingService;
72
73 /**
74 * Name of this argument
75 *
76 * @var string
77 */
78 protected $name = '';
79
80 /**
81 * Short name of this argument
82 *
83 * @var string
84 */
85 protected $shortName = NULL;
86
87 /**
88 * Data type of this argument's value
89 *
90 * @var string
91 */
92 protected $dataType = NULL;
93
94 /**
95 * If the data type is an object, the class schema of the data type class is resolved
96 *
97 * @var \TYPO3\CMS\Extbase\Reflection\ClassSchema
98 */
99 protected $dataTypeClassSchema;
100
101 /**
102 * TRUE if this argument is required
103 *
104 * @var boolean
105 */
106 protected $isRequired = FALSE;
107
108 /**
109 * Actual value of this argument
110 *
111 * @var object
112 */
113 protected $value = NULL;
114
115 /**
116 * Default value. Used if argument is optional.
117 *
118 * @var mixed
119 */
120 protected $defaultValue = NULL;
121
122 /**
123 * A custom validator, used supplementary to the base validation
124 *
125 * @var \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface
126 */
127 protected $validator = NULL;
128
129 /**
130 * The validation results. This can be asked if the argument has errors.
131 *
132 * @var \TYPO3\CMS\Extbase\Error\Result
133 */
134 protected $validationResults = NULL;
135
136 /**
137 * Uid for the argument, if it has one
138 *
139 * @var string
140 */
141 protected $uid = NULL;
142
143 const ORIGIN_CLIENT = 0;
144 const ORIGIN_PERSISTENCE = 1;
145 const ORIGIN_PERSISTENCE_AND_MODIFIED = 2;
146 const ORIGIN_NEWLY_CREATED = 3;
147
148 /**
149 * The origin of the argument value. This is only meaningful after argument mapping.
150 *
151 * One of the ORIGIN_* constants above
152 *
153 * @var integer
154 */
155 protected $origin = 0;
156
157 /**
158 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
159 * @inject
160 */
161 protected $persistenceManager;
162
163 /**
164 * Constructs this controller argument
165 *
166 * @param string $name Name of this argument
167 * @param string $dataType The data type of this argument
168 * @throws \InvalidArgumentException if $name is not a string or empty
169 * @api
170 */
171 public function __construct($name, $dataType) {
172 if (!is_string($name)) {
173 throw new \InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688);
174 }
175 if (strlen($name) === 0) {
176 throw new \InvalidArgumentException('$name must be a non-empty string, ' . strlen($name) . ' characters given.', 1232551853);
177 }
178 $this->name = $name;
179 $this->dataType = $dataType;
180 }
181
182 /**
183 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
184 * @return void
185 */
186 public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService) {
187 $this->reflectionService = $reflectionService;
188 // Check for classnames (which have at least one underscore or backslash)
189 $this->dataTypeClassSchema = strpbrk($this->dataType, '_\\') !== FALSE ? $this->reflectionService->getClassSchema($this->dataType) : NULL;
190 }
191
192 /**
193 * @param \TYPO3\CMS\Extbase\Service\TypeHandlingService $typeHandlingService
194 * @return void
195 */
196 public function injectTypeHandlingService(\TYPO3\CMS\Extbase\Service\TypeHandlingService $typeHandlingService) {
197 $this->typeHandlingService = $typeHandlingService;
198 $this->dataType = $this->typeHandlingService->normalizeType($this->dataType);
199 }
200
201 /**
202 * Returns the name of this argument
203 *
204 * @return string This argument's name
205 * @api
206 */
207 public function getName() {
208 return $this->name;
209 }
210
211 /**
212 * Sets the short name of this argument.
213 *
214 * @param string $shortName A "short name" - a single character
215 * @throws \InvalidArgumentException if $shortName is not a character
216 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this
217 * @api
218 */
219 public function setShortName($shortName) {
220 if ($shortName !== NULL && (!is_string($shortName) || strlen($shortName) !== 1)) {
221 throw new \InvalidArgumentException('$shortName must be a single character or NULL', 1195824959);
222 }
223 $this->shortName = $shortName;
224 return $this;
225 }
226
227 /**
228 * Returns the short name of this argument
229 *
230 * @return string This argument's short name
231 * @api
232 */
233 public function getShortName() {
234 return $this->shortName;
235 }
236
237 /**
238 * Sets the data type of this argument's value
239 *
240 * @param string $dataType The data type. Can be either a built-in type such as "Text" or "Integer" or a fully qualified object name
241 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this
242 * @api
243 */
244 public function setDataType($dataType) {
245 $this->dataType = $dataType;
246 $this->dataTypeClassSchema = $this->reflectionService->getClassSchema($dataType);
247 return $this;
248 }
249
250 /**
251 * Returns the data type of this argument's value
252 *
253 * @return string The data type
254 * @api
255 */
256 public function getDataType() {
257 return $this->dataType;
258 }
259
260 /**
261 * Marks this argument to be required
262 *
263 * @param boolean $required TRUE if this argument should be required
264 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this
265 * @api
266 */
267 public function setRequired($required) {
268 $this->isRequired = (boolean) $required;
269 return $this;
270 }
271
272 /**
273 * Returns TRUE if this argument is required
274 *
275 * @return boolean TRUE if this argument is required
276 * @api
277 */
278 public function isRequired() {
279 return $this->isRequired;
280 }
281
282 /**
283 * Sets the default value of the argument
284 *
285 * @param mixed $defaultValue Default value
286 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument $this
287 * @api
288 */
289 public function setDefaultValue($defaultValue) {
290 $this->defaultValue = $defaultValue;
291 return $this;
292 }
293
294 /**
295 * Returns the default value of this argument
296 *
297 * @return mixed The default value
298 * @api
299 */
300 public function getDefaultValue() {
301 return $this->defaultValue;
302 }
303
304 /**
305 * Sets a custom validator which is used supplementary to the base validation
306 *
307 * @param \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator The actual validator object
308 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument Returns $this (used for fluent interface)
309 * @api
310 */
311 public function setValidator(\TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface $validator) {
312 $this->validator = $validator;
313 return $this;
314 }
315
316 /**
317 * Create and set a validator chain
318 *
319 * @param array $objectNames Object names of the validators
320 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument Returns $this (used for fluent interface)
321 * @api
322 * @deprecated since Extbase 1.4.0, will be removed two versions after Extbase 6.1
323 */
324 public function setNewValidatorConjunction(array $objectNames) {
325 if ($this->validator === NULL) {
326 $this->validator = $this->objectManager->get('TYPO3\\CMS\\Extbase\\Validation\\Validator\\ConjunctionValidator');
327 }
328 foreach ($objectNames as $objectName) {
329 if (!class_exists($objectName)) {
330 $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName;
331 }
332 $this->validator->addValidator($this->objectManager->get($objectName));
333 }
334 return $this;
335 }
336
337 /**
338 * Returns the set validator
339 *
340 * @return \TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface The set validator, NULL if none was set
341 * @api
342 */
343 public function getValidator() {
344 return $this->validator;
345 }
346
347 /**
348 * Get the origin of the argument value. This is only meaningful after argument mapping.
349 *
350 * @return integer one of the ORIGIN_* constants
351 * @deprecated since Extbase 1.4.0, will be removed two versions after Extbase 6.1
352 */
353 public function getOrigin() {
354 return $this->origin;
355 }
356
357 /**
358 * Sets the value of this argument.
359 *
360 * @param mixed $rawValue The value of this argument
361 * @return \TYPO3\CMS\Extbase\Mvc\Controller\Argument
362 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException if the argument is not a valid object of type $dataType
363 */
364 public function setValue($rawValue) {
365 if ($this->configurationManager->isFeatureEnabled('rewrittenPropertyMapper')) {
366 if ($rawValue === NULL) {
367 $this->value = NULL;
368 return $this;
369 }
370 if (is_object($rawValue) && $rawValue instanceof $this->dataType) {
371 $this->value = $rawValue;
372 return $this;
373 }
374 $this->value = $this->propertyMapper->convert($rawValue, $this->dataType, $this->propertyMappingConfiguration);
375 $this->validationResults = $this->propertyMapper->getMessages();
376 if ($this->validator !== NULL) {
377 // TODO: Validation API has also changed!!!
378 $validationMessages = $this->validator->validate($this->value);
379 $this->validationResults->merge($validationMessages);
380 }
381 return $this;
382 } else {
383 if ($rawValue === NULL || is_object($rawValue) && $rawValue instanceof $this->dataType) {
384 $this->value = $rawValue;
385 } else {
386 $this->value = $this->transformValue($rawValue);
387 }
388 return $this;
389 }
390 }
391
392 /**
393 * Checks if the value is a UUID or an array but should be an object, i.e.
394 * the argument's data type class schema is set. If that is the case, this
395 * method tries to look up the corresponding object instead.
396 *
397 * Additionally, it maps arrays to objects in case it is a normal object.
398 *
399 * @param mixed $value The value of an argument
400 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException
401 * @return mixed
402 * @deprecated since Extbase 1.4.0, will be removed two versions after Extbase 6.1
403 */
404 protected function transformValue($value) {
405 if (!class_exists($this->dataType)) {
406 return $value;
407 }
408 $transformedValue = NULL;
409 if ($this->dataTypeClassSchema !== NULL) {
410 // The target object is an Entity or ValueObject.
411 if (is_numeric($value)) {
412 $this->origin = self::ORIGIN_PERSISTENCE;
413 $transformedValue = $this->findObjectByUid($value);
414 } elseif (is_array($value)) {
415 $this->origin = self::ORIGIN_PERSISTENCE_AND_MODIFIED;
416 $transformedValue = $this->deprecatedPropertyMapper->map(array_keys($value), $value, $this->dataType);
417 }
418 } else {
419 if (!is_array($value)) {
420 throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException('The value was a simple type, so we could not map it to an object. Maybe the @entity or @valueobject annotations are missing?', 1251730701);
421 }
422 $this->origin = self::ORIGIN_NEWLY_CREATED;
423 $transformedValue = $this->deprecatedPropertyMapper->map(array_keys($value), $value, $this->dataType);
424 }
425 if (!$transformedValue instanceof $this->dataType && ($transformedValue !== NULL || $this->isRequired())) {
426 throw new \TYPO3\CMS\Extbase\Mvc\Exception\InvalidArgumentValueException('The value must be of type "' . $this->dataType . '", but was of type "' . (is_object($transformedValue) ? get_class($transformedValue) : gettype($transformedValue)) . '".' . ($this->deprecatedPropertyMapper->getMappingResults()->hasErrors() ? '<p>' . implode('<br />', $this->deprecatedPropertyMapper->getMappingResults()->getErrors()) . '</p>' : ''), 1251730702);
427 }
428 return $transformedValue;
429 }
430
431 /**
432 * Finds an object from the repository by searching for its technical UID.
433 *
434 * @param integer $uid The object's uid
435 * @return object Either the object matching the uid or, if none or more than one object was found, NULL
436 */
437 protected function findObjectByUid($uid) {
438 $query = $this->queryFactory->create($this->dataType);
439 $query->getQuerySettings()->setRespectSysLanguage(FALSE);
440 $query->getQuerySettings()->setRespectStoragePage(FALSE);
441 return $query->matching($query->equals('uid', $uid))->execute()->getFirst();
442 }
443
444 /**
445 * Returns the value of this argument
446 *
447 * @return object The value of this argument - if none was set, NULL is returned
448 * @api
449 */
450 public function getValue() {
451 if ($this->value === NULL) {
452 return $this->defaultValue;
453 } else {
454 return $this->value;
455 }
456 }
457
458 /**
459 * Checks if this argument has a value set.
460 *
461 * @return boolean TRUE if a value was set, otherwise FALSE
462 * @deprecated since Extbase 1.4.0, will be removed two versions after Extbase 6.1
463 */
464 public function isValue() {
465 return $this->value !== NULL;
466 }
467
468 /**
469 * Return the Property Mapping Configuration used for this argument; can be used by the initialize*action to modify the Property Mapping.
470 *
471 * @return \TYPO3\CMS\Extbase\Mvc\Controller\MvcPropertyMappingConfiguration
472 * @api
473 */
474 public function getPropertyMappingConfiguration() {
475 return $this->propertyMappingConfiguration;
476 }
477
478 /**
479 * @return boolean TRUE if the argument is valid, FALSE otherwise
480 * @api
481 */
482 public function isValid() {
483 return !$this->validationResults->hasErrors();
484 }
485
486 /**
487 * @return array<\TYPO3\CMS\Extbase\Error\Result> Validation errors which have occurred.
488 * @api
489 */
490 public function getValidationResults() {
491 return $this->validationResults;
492 }
493
494 /**
495 * Returns a string representation of this argument's value
496 *
497 * @return string
498 * @api
499 */
500 public function __toString() {
501 return (string) $this->value;
502 }
503 }