[!!!][+BUGFIX] Extbase (Persistence): All methods trying to find an object by uid...
[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_Persistence_QueryFactory
41 */
42 protected $queryFactory;
43
44 /**
45 * @var Tx_Extbase_Property_Mapper
46 */
47 protected $propertyMapper;
48
49 /**
50 * @var Tx_Extbase_Reflection_Service
51 */
52 protected $reflectionService;
53
54 /**
55 * Name of this argument
56 * @var string
57 */
58 protected $name = '';
59
60 /**
61 * Short name of this argument
62 * @var string
63 */
64 protected $shortName = NULL;
65
66 /**
67 * Data type of this argument's value
68 * @var string
69 */
70 protected $dataType = NULL;
71
72 /**
73 * If the data type is an object, the class schema of the data type class is resolved
74 * @var Tx_Extbase_Reflection_ClassSchema
75 */
76 protected $dataTypeClassSchema;
77
78 /**
79 * TRUE if this argument is required
80 * @var boolean
81 */
82 protected $isRequired = FALSE;
83
84 /**
85 * Actual value of this argument
86 * @var object
87 */
88 protected $value = NULL;
89
90 /**
91 * Default value. Used if argument is optional.
92 * @var mixed
93 */
94 protected $defaultValue = NULL;
95
96 /**
97 * A custom validator, used supplementary to the base validation
98 * @var Tx_Extbase_Validation_Validator_ValidatorInterface
99 */
100 protected $validator = NULL;
101
102 /**
103 * Uid for the argument, if it has one
104 * @var string
105 */
106 protected $uid = NULL;
107
108 const ORIGIN_CLIENT = 0;
109 const ORIGIN_PERSISTENCE = 1;
110 const ORIGIN_PERSISTENCE_AND_MODIFIED = 2;
111 const ORIGIN_NEWLY_CREATED = 3;
112
113 /**
114 * The origin of the argument value. This is only meaningful after argument mapping.
115 *
116 * One of the ORIGIN_* constants above
117 * @var integer
118 */
119 protected $origin = 0;
120
121 /**
122 * Constructs this controller argument
123 *
124 * @param string $name Name of this argument
125 * @param string $dataType The data type of this argument
126 * @throws InvalidArgumentException if $name is not a string or empty
127 * @api
128 */
129 public function __construct($name, $dataType) {
130 if (!is_string($name)) throw new InvalidArgumentException('$name must be of type string, ' . gettype($name) . ' given.', 1187951688);
131 if (strlen($name) === 0) throw new InvalidArgumentException('$name must be a non-empty string, ' . strlen($name) . ' characters given.', 1232551853);
132 $this->name = $name;
133 $this->dataType = $dataType;
134 }
135
136 /**
137 * Initializes this object
138 *
139 * @return void
140 */
141 public function initializeObject() {
142 $this->reflectionService = t3lib_div::makeInstance('Tx_Extbase_Reflection_Service');
143 $this->propertyMapper = t3lib_div::makeInstance('Tx_Extbase_Property_Mapper');
144 $this->propertyMapper->injectReflectionService($this->reflectionService);
145 $this->dataTypeClassSchema = (strstr($this->dataType, '_') !== FALSE) ? $this->reflectionService->getClassSchema($this->dataType) : NULL;
146 }
147
148 /**
149 * Injects the Persistence Manager
150 *
151 * @param Tx_Extbase_Persistence_ManagerInterface
152 * @return void
153 */
154 public function injectPersistenceManager(Tx_Extbase_Persistence_ManagerInterface $persistenceManager) {
155 $this->persistenceManager = $persistenceManager;
156 }
157
158 /**
159 * Injects a QueryFactory instance
160 *
161 * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
162 * @return void
163 */
164 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
165 $this->queryFactory = $queryFactory;
166 }
167
168 /**
169 * Returns the name of this argument
170 *
171 * @return string This argument's name
172 * @api
173 */
174 public function getName() {
175 return $this->name;
176 }
177
178 /**
179 * Sets the short name of this argument.
180 *
181 * @param string $shortName A "short name" - a single character
182 * @return Tx_Extbase_MVC_Controller_Argument $this
183 * @throws InvalidArgumentException if $shortName is not a character
184 * @api
185 */
186 public function setShortName($shortName) {
187 if ($shortName !== NULL && (!is_string($shortName) || strlen($shortName) !== 1)) throw new InvalidArgumentException('$shortName must be a single character or NULL', 1195824959);
188 $this->shortName = $shortName;
189 return $this;
190 }
191
192 /**
193 * Returns the short name of this argument
194 *
195 * @return string This argument's short name
196 * @api
197 */
198 public function getShortName() {
199 return $this->shortName;
200 }
201
202 /**
203 * Sets the data type of this argument's value
204 *
205 * @param string $dataType The data type. Can be either a built-in type such as "Text" or "Integer" or a fully qualified object name
206 * @return Tx_Extbase_MVC_Controller_Argument $this
207 * @api
208 */
209 public function setDataType($dataType) {
210 $this->dataType = $dataType;
211 $this->dataTypeClassSchema = $this->reflectionService->getClassSchema($dataType);
212 return $this;
213 }
214
215 /**
216 * Returns the data type of this argument's value
217 *
218 * @return string The data type
219 * @api
220 */
221 public function getDataType() {
222 return $this->dataType;
223 }
224
225 /**
226 * Marks this argument to be required
227 *
228 * @param boolean $required TRUE if this argument should be required
229 * @return Tx_Extbase_MVC_Controller_Argument $this
230 * @api
231 */
232 public function setRequired($required) {
233 $this->isRequired = (boolean)$required;
234 return $this;
235 }
236
237 /**
238 * Returns TRUE if this argument is required
239 *
240 * @return boolean TRUE if this argument is required
241 * @api
242 */
243 public function isRequired() {
244 return $this->isRequired;
245 }
246
247 /**
248 * Sets the default value of the argument
249 *
250 * @param mixed $defaultValue Default value
251 * @return void
252 * @api
253 */
254 public function setDefaultValue($defaultValue) {
255 $this->defaultValue = $defaultValue;
256 }
257
258 /**
259 * Returns the default value of this argument
260 *
261 * @return mixed The default value
262 * @api
263 */
264 public function getDefaultValue() {
265 return $this->defaultValue;
266 }
267
268 /**
269 * Sets a custom validator which is used supplementary to the base validation
270 *
271 * @param Tx_Extbase_Validation_Validator_ValidatorInterface $validator The actual validator object
272 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
273 * @api
274 */
275 public function setValidator(Tx_Extbase_Validation_Validator_ValidatorInterface $validator) {
276 $this->validator = $validator;
277 return $this;
278 }
279
280 /**
281 * Create and set a validator chain
282 *
283 * @param array Object names of the validators
284 * @return Tx_Extbase_MVC_Controller_Argument Returns $this (used for fluent interface)
285 * @api
286 */
287 public function setNewValidatorConjunction(array $objectNames) {
288 if ($this->validator === NULL) {
289 $this->validator = t3lib_div::makeInstance('Tx_Extbase_Validation_Validator_ConjunctionValidator');
290 }
291 foreach ($objectNames as $objectName) {
292 if (!class_exists($objectName)) $objectName = 'Tx_Extbase_Validation_Validator_' . $objectName;
293 $this->validator->addValidator(t3lib_div::makeInstance($objectName));
294 }
295 return $this;
296 }
297
298 /**
299 * Returns the set validator
300 *
301 * @return Tx_Extbase_Validation_Validator_ValidatorInterface The set validator, NULL if none was set
302 * @api
303 */
304 public function getValidator() {
305 return $this->validator;
306 }
307
308 /**
309 * Get the origin of the argument value. This is only meaningful after argument mapping.
310 *
311 * @return integer one of the ORIGIN_* constants
312 * @author Sebastian Kurf├╝rst <sebastian@typo3.org>
313 */
314 public function getOrigin() {
315 return $this->origin;
316 }
317
318 /**
319 * Sets the value of this argument.
320 *
321 * @param mixed $value: The value of this argument
322 * @return Tx_Extbase_MVC_Controller_Argument $this
323 * @throws Tx_Extbase_MVC_Exception_InvalidArgumentValue if the argument is not a valid object of type $dataType
324 */
325 public function setValue($value) {
326 $this->value = $this->transformValue($value);
327
328 return $this;
329 }
330
331 /**
332 * Checks if the value is a UUID or an array but should be an object, i.e.
333 * the argument's data type class schema is set. If that is the case, this
334 * method tries to look up the corresponding object instead.
335 *
336 * Additionally, it maps arrays to objects in case it is a normal object.
337 *
338 * @param mixed $value The value of an argument
339 * @return mixed
340 */
341 protected function transformValue($value) {
342 if ($value === NULL) {
343 return NULL;
344 }
345 if (!class_exists($this->dataType)) {
346 return $value;
347 }
348 $transformedValue = NULL;
349 if ($this->dataTypeClassSchema !== NULL) {
350 // The target object is an Entity or ValueObject.
351 if (is_numeric($value)) {
352 $this->origin = self::ORIGIN_PERSISTENCE;
353 $transformedValue = $this->findObjectByUid($value);
354 } elseif (is_array($value)) {
355 $this->origin = self::ORIGIN_PERSISTENCE_AND_MODIFIED;
356 $transformedValue = $this->propertyMapper->map(array_keys($value), $value, $this->dataType);
357 }
358 } else {
359 if (!is_array($value)) {
360 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);
361 }
362 $this->origin = self::ORIGIN_NEWLY_CREATED;
363 $transformedValue = $this->propertyMapper->map(array_keys($value), $value, $this->dataType);
364 }
365
366 if (!($transformedValue instanceof $this->dataType)) {
367 throw new Tx_Extbase_MVC_Exception_InvalidArgumentValue('The value must be of type "' . $this->dataType . '", but was of type "' . (is_object($transformedValue) ? get_class($transformedValue) : gettype($transformedValue)) . '".', 1251730701);
368 }
369 return $transformedValue;
370 }
371
372 /**
373 * Finds an object from the repository by searching for its technical UID.
374 *
375 * @param int $uid The object's uid
376 * @return mixed Either the object matching the uid or, if none or more than one object was found, FALSE
377 */
378 protected function findObjectByUid($uid) {
379 $query = $this->queryFactory->create($this->dataType);
380 $query->getQuerySettings()->setRespectSysLanguage(FALSE);
381 $query->getQuerySettings()->setRespectStoragePage(FALSE);
382 $result = $query->matching($query->equals('uid', $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 ?>