[TASK] Clean up DataMapper::convertClassNameToTableName
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Generic / Mapper / DataMapper.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic\Mapper;
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 use TYPO3\CMS\Extbase\Object\Exception\CannotReconstituteObjectException;
18 use TYPO3\CMS\Extbase\Persistence;
19 use TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnexpectedTypeException;
20 use TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface;
21 use TYPO3\CMS\Extbase\Utility\TypeHandlingUtility;
22
23 /**
24 * A mapper to map database tables configured in $TCA on domain objects.
25 */
26 class DataMapper implements \TYPO3\CMS\Core\SingletonInterface {
27
28 /**
29 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
30 * @inject
31 */
32 protected $reflectionService;
33
34 /**
35 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
36 * @inject
37 */
38 protected $qomFactory;
39
40 /**
41 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
42 * @inject
43 */
44 protected $persistenceSession;
45
46 /**
47 * A reference to the page select object providing methods to perform language and work space overlays
48 *
49 * @var \TYPO3\CMS\Frontend\Page\PageRepository
50 */
51 protected $pageSelectObject;
52
53 /**
54 * Cached data maps
55 *
56 * @var array
57 */
58 protected $dataMaps = array();
59
60 /**
61 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapFactory
62 * @inject
63 */
64 protected $dataMapFactory;
65
66 /**
67 * @var \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface
68 * @inject
69 */
70 protected $queryFactory;
71
72 /**
73 * The TYPO3 reference index object
74 *
75 * @var \TYPO3\CMS\Core\Database\ReferenceIndex
76 */
77 protected $referenceIndex;
78
79 /**
80 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
81 * @inject
82 */
83 protected $objectManager;
84
85 /**
86 * Maps the given rows on objects
87 *
88 * @param string $className The name of the class
89 * @param array $rows An array of arrays with field_name => value pairs
90 * @return array An array of objects of the given class
91 */
92 public function map($className, array $rows) {
93 $objects = array();
94 foreach ($rows as $row) {
95 $objects[] = $this->mapSingleRow($this->getTargetType($className, $row), $row);
96 }
97 return $objects;
98 }
99
100 /**
101 * Returns the target type for the given row.
102 *
103 * @param string $className The name of the class
104 * @param array $row A single array with field_name => value pairs
105 * @return string The target type (a class name)
106 */
107 public function getTargetType($className, array $row) {
108 $dataMap = $this->getDataMap($className);
109 $targetType = $className;
110 if ($dataMap->getRecordTypeColumnName() !== NULL) {
111 foreach ($dataMap->getSubclasses() as $subclassName) {
112 $recordSubtype = $this->getDataMap($subclassName)->getRecordType();
113 if ($row[$dataMap->getRecordTypeColumnName()] === $recordSubtype) {
114 $targetType = $subclassName;
115 break;
116 }
117 }
118 }
119 return $targetType;
120 }
121
122 /**
123 * Maps a single row on an object of the given class
124 *
125 * @param string $className The name of the target class
126 * @param array $row A single array with field_name => value pairs
127 * @return object An object of the given class
128 */
129 protected function mapSingleRow($className, array $row) {
130 if ($this->persistenceSession->hasIdentifier($row['uid'], $className)) {
131 $object = $this->persistenceSession->getObjectByIdentifier($row['uid'], $className);
132 } else {
133 $object = $this->createEmptyObject($className);
134 $this->thawProperties($object, $row);
135 $object->_memorizeCleanState();
136 $this->persistenceSession->registerObject($object, $row['uid']);
137 $this->persistenceSession->registerReconstitutedEntity($object);
138 }
139 return $object;
140 }
141
142 /**
143 * Creates a skeleton of the specified object
144 *
145 * @param string $className Name of the class to create a skeleton for
146 * @throws CannotReconstituteObjectException
147 * @return object The object skeleton
148 */
149 protected function createEmptyObject($className) {
150 // Note: The class_implements() function also invokes autoload to assure that the interfaces
151 // and the class are loaded. Would end up with __PHP_Incomplete_Class without it.
152 if (!in_array(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface::class, class_implements($className))) {
153 throw new CannotReconstituteObjectException('Cannot create empty instance of the class "' . $className
154 . '" because it does not implement the TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface.', 1234386924);
155 }
156 $object = $this->objectManager->getEmptyObject($className);
157 return $object;
158 }
159
160 /**
161 * Sets the given properties on the object.
162 *
163 * @param DomainObjectInterface $object The object to set properties on
164 * @param array $row
165 * @return void
166 */
167 protected function thawProperties(DomainObjectInterface $object, array $row) {
168 $className = get_class($object);
169 $classSchema = $this->reflectionService->getClassSchema($className);
170 $dataMap = $this->getDataMap($className);
171 $object->_setProperty('uid', (int)$row['uid']);
172 $object->_setProperty('pid', (int)$row['pid']);
173 $object->_setProperty('_localizedUid', (int)$row['uid']);
174 $object->_setProperty('_versionedUid', (int)$row['uid']);
175 if ($dataMap->getLanguageIdColumnName() !== NULL) {
176 $object->_setProperty('_languageUid', (int)$row[$dataMap->getLanguageIdColumnName()]);
177 if (isset($row['_LOCALIZED_UID'])) {
178 $object->_setProperty('_localizedUid', (int)$row['_LOCALIZED_UID']);
179 }
180 }
181 if (!empty($row['_ORIG_uid']) && !empty($GLOBALS['TCA'][$dataMap->getTableName()]['ctrl']['versioningWS'])) {
182 $object->_setProperty('_versionedUid', (int)$row['_ORIG_uid']);
183 }
184 $properties = $object->_getProperties();
185 foreach ($properties as $propertyName => $propertyValue) {
186 if (!$dataMap->isPersistableProperty($propertyName)) {
187 continue;
188 }
189 $columnMap = $dataMap->getColumnMap($propertyName);
190 $columnName = $columnMap->getColumnName();
191 $propertyData = $classSchema->getProperty($propertyName);
192 $propertyValue = NULL;
193 if ($row[$columnName] !== NULL) {
194 switch ($propertyData['type']) {
195 case 'integer':
196 $propertyValue = (int)$row[$columnName];
197 break;
198 case 'float':
199 $propertyValue = (double)$row[$columnName];
200 break;
201 case 'boolean':
202 $propertyValue = (bool)$row[$columnName];
203 break;
204 case 'string':
205 $propertyValue = (string)$row[$columnName];
206 break;
207 case 'array':
208 // $propertyValue = $this->mapArray($row[$columnName]); // Not supported, yet!
209 break;
210 case 'SplObjectStorage':
211 case \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class:
212 $propertyValue = $this->mapResultToPropertyValue(
213 $object,
214 $propertyName,
215 $this->fetchRelated($object, $propertyName, $row[$columnName])
216 );
217 break;
218 default:
219 if ($propertyData['type'] === 'DateTime' || in_array('DateTime', class_parents($propertyData['type']))) {
220 $propertyValue = $this->mapDateTime($row[$columnName], $columnMap->getDateTimeStorageFormat());
221 } elseif (TypeHandlingUtility::isCoreType($propertyData['type'])) {
222 $propertyValue = $this->mapCoreType($propertyData['type'], $row[$columnName]);
223 } else {
224 $propertyValue = $this->mapObjectToClassProperty(
225 $object,
226 $propertyName,
227 $row[$columnName]
228 );
229 }
230
231 }
232 }
233 if ($propertyValue !== NULL) {
234 $object->_setProperty($propertyName, $propertyValue);
235 }
236 }
237 }
238
239 /**
240 * Map value to a core type
241 *
242 * @param string $type
243 * @param mixed $value
244 * @return \TYPO3\CMS\Core\Type\TypeInterface
245 */
246 protected function mapCoreType($type, $value) {
247 return new $type($value);
248 }
249
250 /**
251 * Creates a DateTime from an unix timestamp or date/datetime value.
252 * If the input is empty, NULL is returned.
253 *
254 * @param int|string $value Unix timestamp or date/datetime value
255 * @param NULL|string $storageFormat Storage format for native date/datetime fields
256 * @return \DateTime
257 */
258 protected function mapDateTime($value, $storageFormat = NULL) {
259 if (empty($value) || $value === '0000-00-00' || $value === '0000-00-00 00:00:00') {
260 // 0 -> NULL !!!
261 return NULL;
262 } elseif ($storageFormat === 'date' || $storageFormat === 'datetime') {
263 // native date/datetime values are stored in UTC
264 $utcTimeZone = new \DateTimeZone('UTC');
265 $utcDateTime = new \DateTime($value, $utcTimeZone);
266 $currentTimeZone = new \DateTimeZone(date_default_timezone_get());
267 return $utcDateTime->setTimezone($currentTimeZone);
268 } else {
269 return new \DateTime(date('c', $value));
270 }
271 }
272
273 /**
274 * Fetches a collection of objects related to a property of a parent object
275 *
276 * @param DomainObjectInterface $parentObject The object instance this proxy is part of
277 * @param string $propertyName The name of the proxied property in it's parent
278 * @param mixed $fieldValue The raw field value.
279 * @param bool $enableLazyLoading A flag indication if the related objects should be lazy loaded
280 * @return \TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage|Persistence\QueryResultInterface The result
281 */
282 public function fetchRelated(DomainObjectInterface $parentObject, $propertyName, $fieldValue = '', $enableLazyLoading = TRUE) {
283 $propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
284 if ($enableLazyLoading === TRUE && $propertyMetaData['lazy']) {
285 if ($propertyMetaData['type'] === \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class) {
286 $result = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage::class, $parentObject, $propertyName, $fieldValue);
287 } else {
288 if (empty($fieldValue)) {
289 $result = NULL;
290 } else {
291 $result = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy::class, $parentObject, $propertyName, $fieldValue);
292 }
293 }
294 } else {
295 $result = $this->fetchRelatedEager($parentObject, $propertyName, $fieldValue);
296 }
297 return $result;
298 }
299
300 /**
301 * Fetches the related objects from the storage backend.
302 *
303 * @param DomainObjectInterface $parentObject The object instance this proxy is part of
304 * @param string $propertyName The name of the proxied property in it's parent
305 * @param mixed $fieldValue The raw field value.
306 * @return mixed
307 */
308 protected function fetchRelatedEager(DomainObjectInterface $parentObject, $propertyName, $fieldValue = '') {
309 return $fieldValue === '' ? $this->getEmptyRelationValue($parentObject, $propertyName) : $this->getNonEmptyRelationValue($parentObject, $propertyName, $fieldValue);
310 }
311
312 /**
313 * @param DomainObjectInterface $parentObject
314 * @param string $propertyName
315 * @return array|NULL
316 */
317 protected function getEmptyRelationValue(DomainObjectInterface $parentObject, $propertyName) {
318 $columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
319 $relatesToOne = $columnMap->getTypeOfRelation() == ColumnMap::RELATION_HAS_ONE;
320 return $relatesToOne ? NULL : array();
321 }
322
323 /**
324 * @param DomainObjectInterface $parentObject
325 * @param string $propertyName
326 * @param string $fieldValue
327 * @return Persistence\QueryResultInterface
328 */
329 protected function getNonEmptyRelationValue(DomainObjectInterface $parentObject, $propertyName, $fieldValue) {
330 $query = $this->getPreparedQuery($parentObject, $propertyName, $fieldValue);
331 return $query->execute();
332 }
333
334 /**
335 * Builds and returns the prepared query, ready to be executed.
336 *
337 * @param DomainObjectInterface $parentObject
338 * @param string $propertyName
339 * @param string $fieldValue
340 * @return Persistence\QueryInterface
341 */
342 protected function getPreparedQuery(DomainObjectInterface $parentObject, $propertyName, $fieldValue = '') {
343 $columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
344 $type = $this->getType(get_class($parentObject), $propertyName);
345 $query = $this->queryFactory->create($type);
346 $query->getQuerySettings()->setRespectStoragePage(FALSE);
347 $query->getQuerySettings()->setRespectSysLanguage(FALSE);
348 if ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_MANY) {
349 if ($columnMap->getChildSortByFieldName() !== NULL) {
350 $query->setOrderings(array($columnMap->getChildSortByFieldName() => Persistence\QueryInterface::ORDER_ASCENDING));
351 }
352 } elseif ($columnMap->getTypeOfRelation() === ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
353 $query->setSource($this->getSource($parentObject, $propertyName));
354 if ($columnMap->getChildSortByFieldName() !== NULL) {
355 $query->setOrderings(array($columnMap->getChildSortByFieldName() => Persistence\QueryInterface::ORDER_ASCENDING));
356 }
357 }
358 $query->matching($this->getConstraint($query, $parentObject, $propertyName, $fieldValue, $columnMap->getRelationTableMatchFields()));
359 return $query;
360 }
361
362 /**
363 * Builds and returns the constraint for multi value properties.
364 *
365 * @param Persistence\QueryInterface $query
366 * @param DomainObjectInterface $parentObject
367 * @param string $propertyName
368 * @param string $fieldValue
369 * @param array $relationTableMatchFields
370 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint
371 */
372 protected function getConstraint(Persistence\QueryInterface $query, DomainObjectInterface $parentObject, $propertyName, $fieldValue = '', $relationTableMatchFields = array()) {
373 $columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
374 if ($columnMap->getParentKeyFieldName() !== NULL) {
375 $constraint = $query->equals($columnMap->getParentKeyFieldName(), $parentObject);
376 if ($columnMap->getParentTableFieldName() !== NULL) {
377 $constraint = $query->logicalAnd(
378 $constraint,
379 $query->equals($columnMap->getParentTableFieldName(), $this->getDataMap(get_class($parentObject))->getTableName())
380 );
381 }
382 } else {
383 $constraint = $query->in('uid', \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $fieldValue));
384 }
385 if (count($relationTableMatchFields) > 0) {
386 foreach ($relationTableMatchFields as $relationTableMatchFieldName => $relationTableMatchFieldValue) {
387 $constraint = $query->logicalAnd($constraint, $query->equals($relationTableMatchFieldName, $relationTableMatchFieldValue));
388 }
389 }
390 return $constraint;
391 }
392
393 /**
394 * Builds and returns the source to build a join for a m:n relation.
395 *
396 * @param DomainObjectInterface $parentObject
397 * @param string $propertyName
398 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source
399 */
400 protected function getSource(DomainObjectInterface $parentObject, $propertyName) {
401 $columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
402 $left = $this->qomFactory->selector(NULL, $columnMap->getRelationTableName());
403 $childClassName = $this->getType(get_class($parentObject), $propertyName);
404 $right = $this->qomFactory->selector($childClassName, $columnMap->getChildTableName());
405 $joinCondition = $this->qomFactory->equiJoinCondition($columnMap->getRelationTableName(), $columnMap->getChildKeyFieldName(), $columnMap->getChildTableName(), 'uid');
406 $source = $this->qomFactory->join($left, $right, Persistence\Generic\Query::JCR_JOIN_TYPE_INNER, $joinCondition);
407 return $source;
408 }
409
410 /**
411 * Returns the mapped classProperty from the identiyMap or
412 * mapResultToPropertyValue()
413 *
414 * If the field value is empty and the column map has no parent key field name,
415 * the relation will be empty. If the persistence session has a registered object of
416 * the correct type and identity (fieldValue), this function returns that object.
417 * Otherwise, it proceeds with mapResultToPropertyValue().
418 *
419 * @param DomainObjectInterface $parentObject
420 * @param string $propertyName
421 * @param mixed $fieldValue the raw field value
422 * @return mixed
423 * @see mapResultToPropertyValue()
424 */
425 protected function mapObjectToClassProperty(DomainObjectInterface $parentObject, $propertyName, $fieldValue) {
426 if ($this->propertyMapsByForeignKey($parentObject, $propertyName)) {
427 $result = $this->fetchRelated($parentObject, $propertyName, $fieldValue);
428 $propertyValue = $this->mapResultToPropertyValue($parentObject, $propertyName, $result);
429 } else {
430 if ($fieldValue === '') {
431 $propertyValue = $this->getEmptyRelationValue($parentObject, $propertyName);
432 } else {
433 $propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
434 if ($this->persistenceSession->hasIdentifier($fieldValue, $propertyMetaData['type'])) {
435 $propertyValue = $this->persistenceSession->getObjectByIdentifier($fieldValue, $propertyMetaData['type']);
436 } else {
437 $result = $this->fetchRelated($parentObject, $propertyName, $fieldValue);
438 $propertyValue = $this->mapResultToPropertyValue($parentObject, $propertyName, $result);
439 }
440 }
441 }
442
443 return $propertyValue;
444 }
445
446 /**
447 * Checks if the relation is based on a foreign key.
448 *
449 * @param DomainObjectInterface $parentObject
450 * @param string $propertyName
451 * @return bool TRUE if the property is mapped
452 */
453 protected function propertyMapsByForeignKey(DomainObjectInterface $parentObject, $propertyName) {
454 $columnMap = $this->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
455 return ($columnMap->getParentKeyFieldName() !== NULL);
456 }
457
458 /**
459 * Returns the given result as property value of the specified property type.
460 *
461 * @param DomainObjectInterface $parentObject
462 * @param string $propertyName
463 * @param mixed $result The result
464 * @return mixed
465 */
466 public function mapResultToPropertyValue(DomainObjectInterface $parentObject, $propertyName, $result) {
467 $propertyValue = NULL;
468 if ($result instanceof Persistence\Generic\LoadingStrategyInterface) {
469 $propertyValue = $result;
470 } else {
471 $propertyMetaData = $this->reflectionService->getClassSchema(get_class($parentObject))->getProperty($propertyName);
472 if (in_array($propertyMetaData['type'], array('array', 'ArrayObject', 'SplObjectStorage', \TYPO3\CMS\Extbase\Persistence\ObjectStorage::class), TRUE)) {
473 $objects = array();
474 foreach ($result as $value) {
475 $objects[] = $value;
476 }
477 if ($propertyMetaData['type'] === 'ArrayObject') {
478 $propertyValue = new \ArrayObject($objects);
479 } elseif (in_array($propertyMetaData['type'], array(\TYPO3\CMS\Extbase\Persistence\ObjectStorage::class), TRUE)) {
480 $propertyValue = new Persistence\ObjectStorage();
481 foreach ($objects as $object) {
482 $propertyValue->attach($object);
483 }
484 $propertyValue->_memorizeCleanState();
485 } else {
486 $propertyValue = $objects;
487 }
488 } elseif (strpbrk($propertyMetaData['type'], '_\\') !== FALSE) {
489 if (is_object($result) && $result instanceof Persistence\QueryResultInterface) {
490 $propertyValue = $result->getFirst();
491 } else {
492 $propertyValue = $result;
493 }
494 }
495 }
496 return $propertyValue;
497 }
498
499 /**
500 * Counts the number of related objects assigned to a property of a parent object
501 *
502 * @param DomainObjectInterface $parentObject The object instance this proxy is part of
503 * @param string $propertyName The name of the proxied property in it's parent
504 * @param mixed $fieldValue The raw field value.
505 * @return int
506 */
507 public function countRelated(DomainObjectInterface $parentObject, $propertyName, $fieldValue = '') {
508 $query = $this->getPreparedQuery($parentObject, $propertyName, $fieldValue);
509 return $query->execute()->count();
510 }
511
512 /**
513 * Delegates the call to the Data Map.
514 * Returns TRUE if the property is persistable (configured in $TCA)
515 *
516 * @param string $className The property name
517 * @param string $propertyName The property name
518 * @return bool TRUE if the property is persistable (configured in $TCA)
519 */
520 public function isPersistableProperty($className, $propertyName) {
521 $dataMap = $this->getDataMap($className);
522 return $dataMap->isPersistableProperty($propertyName);
523 }
524
525 /**
526 * Returns a data map for a given class name
527 *
528 * @param string $className The class name you want to fetch the Data Map for
529 * @throws Persistence\Generic\Exception
530 * @return DataMap The data map
531 */
532 public function getDataMap($className) {
533 if (!is_string($className) || $className === '') {
534 throw new Persistence\Generic\Exception('No class name was given to retrieve the Data Map for.', 1251315965);
535 }
536 if (!isset($this->dataMaps[$className])) {
537 $this->dataMaps[$className] = $this->dataMapFactory->buildDataMap($className);
538 }
539 return $this->dataMaps[$className];
540 }
541
542 /**
543 * Returns the selector (table) name for a given class name.
544 *
545 * @param string $className
546 * @return string The selector name
547 */
548 public function convertClassNameToTableName($className) {
549 return $this->getDataMap($className)->getTableName();
550 }
551
552 /**
553 * Returns the column name for a given property name of the specified class.
554 *
555 * @param string $propertyName
556 * @param string $className
557 * @return string The column name
558 */
559 public function convertPropertyNameToColumnName($propertyName, $className = NULL) {
560 if (!empty($className)) {
561 $dataMap = $this->getDataMap($className);
562 if ($dataMap !== NULL) {
563 $columnMap = $dataMap->getColumnMap($propertyName);
564 if ($columnMap !== NULL) {
565 return $columnMap->getColumnName();
566 }
567 }
568 }
569 return \TYPO3\CMS\Core\Utility\GeneralUtility::camelCaseToLowerCaseUnderscored($propertyName);
570 }
571
572 /**
573 * Returns the type of a child object.
574 *
575 * @param string $parentClassName The class name of the object this proxy is part of
576 * @param string $propertyName The name of the proxied property in it's parent
577 * @throws UnexpectedTypeException
578 * @return string The class name of the child object
579 */
580 public function getType($parentClassName, $propertyName) {
581 $propertyMetaData = $this->reflectionService->getClassSchema($parentClassName)->getProperty($propertyName);
582 if (!empty($propertyMetaData['elementType'])) {
583 $type = $propertyMetaData['elementType'];
584 } elseif (!empty($propertyMetaData['type'])) {
585 $type = $propertyMetaData['type'];
586 } else {
587 throw new UnexpectedTypeException('Could not determine the child object type.', 1251315967);
588 }
589 return $type;
590 }
591
592 /**
593 * Returns a plain value, i.e. objects are flattened out if possible.
594 * Multi value objects or arrays will be converted to a comma-separated list for use in IN SQL queries.
595 *
596 * @param mixed $input The value that will be converted.
597 * @param ColumnMap $columnMap Optional column map for retrieving the date storage format.
598 * @param callable $parseStringValueCallback Optional callback method that will be called for string values. Can be used to do database quotation.
599 * @param array $parseStringValueCallbackParameters Additional parameters that will be passed to the callabck as second parameter.
600 * @throws \InvalidArgumentException
601 * @throws UnexpectedTypeException
602 * @return int|string
603 */
604 public function getPlainValue($input, $columnMap = NULL, $parseStringValueCallback = NULL, array $parseStringValueCallbackParameters = array()) {
605 if ($input === NULL) {
606 return 'NULL';
607 }
608 if ($input instanceof Persistence\Generic\LazyLoadingProxy) {
609 $input = $input->_loadRealInstance();
610 }
611
612 if (is_bool($input)) {
613 $parameter = (int)$input;
614 } elseif ($input instanceof \DateTime) {
615 if (!is_null($columnMap) && !is_null($columnMap->getDateTimeStorageFormat())) {
616 $storageFormat = $columnMap->getDateTimeStorageFormat();
617 switch ($storageFormat) {
618 case 'datetime':
619 $parameter = $input->format('Y-m-d H:i:s');
620 break;
621 case 'date':
622 $parameter = $input->format('Y-m-d');
623 break;
624 default:
625 throw new \InvalidArgumentException('Column map DateTime format "' . $storageFormat . '" is unknown. Allowed values are datetime or date.', 1395353470);
626 }
627 } else {
628 $parameter = $input->format('U');
629 }
630 } elseif (TypeHandlingUtility::isValidTypeForMultiValueComparison($input)) {
631 $plainValueArray = array();
632 foreach ($input as $inputElement) {
633 $plainValueArray[] = $this->getPlainValue($inputElement, $columnMap, $parseStringValueCallback, $parseStringValueCallbackParameters);
634 }
635 $parameter = implode(',', $plainValueArray);
636 } elseif ($input instanceof DomainObjectInterface) {
637 $parameter = (int)$input->getUid();
638 } elseif (is_object($input)) {
639 if (TypeHandlingUtility::isCoreType($input)) {
640 $parameter = $this->getPlainStringValue($input, $parseStringValueCallback, $parseStringValueCallbackParameters);
641 } else {
642 throw new UnexpectedTypeException('An object of class "' . get_class($input) . '" could not be converted to a plain value.', 1274799934);
643 }
644 } else {
645 $parameter = $this->getPlainStringValue($input, $parseStringValueCallback, $parseStringValueCallbackParameters);
646 }
647 return $parameter;
648 }
649
650 /**
651 * If the given callback is set the value will be passed on the the callback function.
652 * The value will be converted to a string.
653 *
654 * @param string $value The string value that should be processed. Will be passed to the callback as first parameter.
655 * @param callable $callback The data passed to call_user_func().
656 * @param array $additionalParameters Optional additional parameters passed to the callback as second argument.
657 * @return string
658 */
659 protected function getPlainStringValue($value, $callback = NULL , array $additionalParameters = array()) {
660 if (is_callable($callback)) {
661 $value = call_user_func($callback, $value, $additionalParameters);
662 }
663 return (string)$value;
664 }
665
666 }