88c761fc7cbe363a738a3fc2146ca3101813578f
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Persistence / Generic / Backend.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Persistence\Generic;
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\DomainObject\DomainObjectInterface;
18 use TYPO3\CMS\Extbase\Persistence\ObjectMonitoringInterface;
19
20 /**
21 * A persistence backend. This backend maps objects to the relational model of the storage backend.
22 * It persists all added, removed and changed objects.
23 */
24 class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface, \TYPO3\CMS\Core\SingletonInterface {
25
26 /**
27 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
28 * @inject
29 */
30 protected $session;
31
32 /**
33 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
34 */
35 protected $persistenceManager;
36
37 /**
38 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
39 */
40 protected $aggregateRootObjects;
41
42 /**
43 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
44 */
45 protected $deletedEntities;
46
47 /**
48 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
49 */
50 protected $changedEntities;
51
52 /**
53 * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
54 */
55 protected $visitedDuringPersistence;
56
57 /**
58 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
59 * @inject
60 */
61 protected $reflectionService;
62
63 /**
64 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
65 * @inject
66 */
67 protected $qomFactory;
68
69 /**
70 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Storage\BackendInterface
71 * @inject
72 */
73 protected $storageBackend;
74
75 /**
76 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
77 * @inject
78 */
79 protected $dataMapper;
80
81 /**
82 * The TYPO3 reference index object
83 *
84 * @var \TYPO3\CMS\Core\Database\ReferenceIndex
85 */
86 protected $referenceIndex;
87
88 /**
89 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
90 */
91 protected $configurationManager;
92
93 /**
94 * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
95 * @inject
96 */
97 protected $signalSlotDispatcher;
98
99 /**
100 * Constructs the backend
101 *
102 * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
103 */
104 public function __construct(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager) {
105 $this->configurationManager = $configurationManager;
106 $this->referenceIndex = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
107 $this->aggregateRootObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
108 $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
109 $this->changedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
110 }
111
112 /**
113 * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
114 */
115 public function setPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager) {
116 $this->persistenceManager = $persistenceManager;
117 }
118
119 /**
120 * Returns the repository session
121 *
122 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Session
123 */
124 public function getSession() {
125 return $this->session;
126 }
127
128 /**
129 * Returns the Data Mapper
130 *
131 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
132 */
133 public function getDataMapper() {
134 return $this->dataMapper;
135 }
136
137 /**
138 * Returns the current QOM factory
139 *
140 * @return \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
141 */
142 public function getQomFactory() {
143 return $this->qomFactory;
144 }
145
146 /**
147 * Returns the reflection service
148 *
149 * @return \TYPO3\CMS\Extbase\Reflection\ReflectionService
150 */
151 public function getReflectionService() {
152 return $this->reflectionService;
153 }
154
155 /**
156 * Returns the number of records matching the query.
157 *
158 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
159 * @return integer
160 * @api
161 */
162 public function getObjectCountByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
163 return $this->storageBackend->getObjectCountByQuery($query);
164 }
165
166 /**
167 * Returns the object data matching the $query.
168 *
169 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
170 * @return array
171 * @api
172 */
173 public function getObjectDataByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
174 $query = $this->emitBeforeGettingObjectDataSignal($query);
175 $result = $this->storageBackend->getObjectDataByQuery($query);
176 $result = $this->emitafterGettingObjectDataSignal($query, $result);
177 return $result;
178 }
179
180 /**
181 * Emits a signal before object data is fetched
182 *
183 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
184 * @return \TYPO3\CMS\Extbase\Persistence\QueryInterface Modified query
185 */
186 protected function emitBeforeGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
187 $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'beforeGettingObjectData', array($query));
188 return $signalArguments[0];
189 }
190
191 /**
192 * Emits a signal after object data is fetched
193 *
194 * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
195 * @param array $result
196 * @return array Modified result
197 */
198 protected function emitAfterGettingObjectDataSignal(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query, array $result) {
199 $signalArguments = $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterGettingObjectData', array($query, $result));
200 return $signalArguments[1];
201 }
202
203 /**
204 * Returns the (internal) identifier for the object, if it is known to the
205 * backend. Otherwise NULL is returned.
206 *
207 * @param object $object
208 * @return string The identifier for the object if it is known, or NULL
209 */
210 public function getIdentifierByObject($object) {
211 if ($object instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
212 $object = $object->_loadRealInstance();
213 if (!is_object($object)) {
214 return NULL;
215 }
216 }
217 return $this->session->getIdentifierByObject($object);
218 }
219
220 /**
221 * Returns the object with the (internal) identifier, if it is known to the
222 * backend. Otherwise NULL is returned.
223 *
224 * @param string $identifier
225 * @param string $className
226 * @return object The object for the identifier if it is known, or NULL
227 */
228 public function getObjectByIdentifier($identifier, $className) {
229 if ($this->session->hasIdentifier($identifier, $className)) {
230 return $this->session->getObjectByIdentifier($identifier, $className);
231 } else {
232 $query = $this->persistenceManager->createQueryForType($className);
233 $query->getQuerySettings()->setRespectStoragePage(FALSE);
234 $query->getQuerySettings()->setRespectSysLanguage(FALSE);
235 return $query->matching($query->equals('uid', $identifier))->execute()->getFirst();
236 }
237 }
238
239 /**
240 * Checks if the given object has ever been persisted.
241 *
242 * @param object $object The object to check
243 * @return boolean TRUE if the object is new, FALSE if the object exists in the repository
244 */
245 public function isNewObject($object) {
246 return $this->getIdentifierByObject($object) === NULL;
247 }
248
249 /**
250 * Replaces the given object by the second object.
251 *
252 * This method will unregister the existing object at the identity map and
253 * register the new object instead. The existing object must therefore
254 * already be registered at the identity map which is the case for all
255 * reconstituted objects.
256 *
257 * The new object will be identified by the uid which formerly belonged
258 * to the existing object. The existing object looses its uid.
259 *
260 * @param object $existingObject The existing object
261 * @param object $newObject The new object
262 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
263 * @return void
264 * @deprecated since 6.1, will be removed two versions later
265 */
266 public function replaceObject($existingObject, $newObject) {
267 $this->session->replaceReconstitutedEntity($existingObject, $newObject);
268 }
269
270 /**
271 * Sets the aggregate root objects
272 *
273 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
274 * @return void
275 */
276 public function setAggregateRootObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects) {
277 $this->aggregateRootObjects = $objects;
278 }
279
280 /**
281 * Sets the changed objects
282 *
283 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
284 * @return void
285 */
286 public function setChangedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities) {
287 $this->changedEntities = $entities;
288 }
289
290 /**
291 * Sets the deleted objects
292 *
293 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
294 * @return void
295 */
296 public function setDeletedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities) {
297 $this->deletedEntities = $entities;
298 }
299
300 /**
301 * Commits the current persistence session.
302 *
303 * @return void
304 */
305 public function commit() {
306 $this->persistObjects();
307 $this->processDeletedObjects();
308 }
309
310 /**
311 * Sets the deleted objects
312 *
313 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
314 * @return void
315 * @deprecated since 6.1, will be removed two versions later
316 */
317 public function setDeletedObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects) {
318 $this->setDeletedEntities($objects);
319 }
320
321 /**
322 * Traverse and persist all aggregate roots and their object graph.
323 *
324 * @return void
325 */
326 protected function persistObjects() {
327 $this->visitedDuringPersistence = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
328 foreach ($this->aggregateRootObjects as $object) {
329 /** @var DomainObjectInterface $object */
330 if ($object->_isNew()) {
331 $this->insertObject($object);
332 }
333 $this->persistObject($object, NULL);
334 }
335 foreach ($this->changedEntities as $object) {
336 $this->persistObject($object, NULL);
337 }
338 }
339
340 /**
341 * Persists the given object.
342 *
343 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be inserted
344 * @return void
345 */
346 protected function persistObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) {
347 if (isset($this->visitedDuringPersistence[$object])) {
348 return;
349 }
350 $row = array();
351 $queue = array();
352 $dataMap = $this->dataMapper->getDataMap(get_class($object));
353 $properties = $object->_getProperties();
354 foreach ($properties as $propertyName => $propertyValue) {
355 if (!$dataMap->isPersistableProperty($propertyName) || $this->propertyValueIsLazyLoaded($propertyValue)) {
356 continue;
357 }
358 $columnMap = $dataMap->getColumnMap($propertyName);
359 if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\ObjectStorage) {
360 $cleanProperty = $object->_getCleanProperty($propertyName);
361 // objectstorage needs to be persisted if the object is new, the objectstorge is dirty, meaning it has
362 // been changed after initial build, or a empty objectstorge is present and the cleanstate objectstorage
363 // has childelements, meaning all elements should been removed from the objectstorage
364 if ($object->_isNew() || $propertyValue->_isDirty() || ($propertyValue->count() == 0 && $cleanProperty && $cleanProperty->count() > 0)) {
365 $this->persistObjectStorage($propertyValue, $object, $propertyName, $row);
366 $propertyValue->_memorizeCleanState();
367 }
368 foreach ($propertyValue as $containedObject) {
369 if ($containedObject instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
370 $queue[] = $containedObject;
371 }
372 }
373 } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface
374 && $object instanceof ObjectMonitoringInterface) {
375 if ($object->_isDirty($propertyName)) {
376 if ($propertyValue->_isNew()) {
377 $this->insertObject($propertyValue, $object, $propertyName);
378 }
379 $row[$columnMap->getColumnName()] = $this->getPlainValue($propertyValue);
380 }
381 $queue[] = $propertyValue;
382 } elseif ($object->_isNew() || $object->_isDirty($propertyName)) {
383 $row[$columnMap->getColumnName()] = $this->getPlainValue($propertyValue, $columnMap);
384 }
385 }
386 if (count($row) > 0) {
387 $this->updateObject($object, $row);
388 $object->_memorizeCleanState();
389 }
390 $this->visitedDuringPersistence[$object] = $object->getUid();
391 foreach ($queue as $queuedObject) {
392 $this->persistObject($queuedObject);
393 }
394 }
395
396 /**
397 * Checks, if the property value is lazy loaded and was not initialized
398 *
399 * @param mixed $propertyValue The property value
400 * @return boolean
401 */
402 protected function propertyValueIsLazyLoaded($propertyValue) {
403 if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy) {
404 return TRUE;
405 }
406 if ($propertyValue instanceof \TYPO3\CMS\Extbase\Persistence\Generic\LazyObjectStorage) {
407 if ($propertyValue->isInitialized() === FALSE) {
408 return TRUE;
409 }
410 }
411 return FALSE;
412 }
413
414 /**
415 * Persists a an object storage. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
416 * gets persisted immediately. Objects which were removed from the property were detached from the parent object. They will not be
417 * deleted by default. You have to annotate the property with "@cascade remove" if you want them to be deleted as well.
418 *
419 * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objectStorage The object storage to be persisted.
420 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object. One of the properties holds the object storage.
421 * @param string $propertyName The name of the property holding the object storage.
422 * @param array &$row The row array of the parent object to be persisted. It's passed by reference and gets filled with either a comma separated list of uids (csv) or the number of contained objects.
423 * @return void
424 */
425 protected function persistObjectStorage(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objectStorage, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, array &$row) {
426 $className = get_class($parentObject);
427 $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
428 $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
429 foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
430 $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
431 if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') {
432 $this->removeEntity($removedObject);
433 }
434 }
435
436 $currentUids = array();
437 $sortingPosition = 1;
438 $updateSortingOfFollowing = FALSE;
439
440 foreach ($objectStorage as $object) {
441 /** @var DomainObjectInterface $object */
442 if (empty($currentUids)) {
443 $sortingPosition = 1;
444 } else {
445 $sortingPosition++;
446 }
447 $cleanProperty = $parentObject->_getCleanProperty($propertyName);
448 if ($object->_isNew()) {
449 $this->insertObject($object);
450 $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
451 // if a new object is inserted, all objects after this need to have their sorting updated
452 $updateSortingOfFollowing = TRUE;
453 } elseif ($cleanProperty === NULL || $cleanProperty->getPosition($object) === NULL) {
454 // if parent object is new then it doesn't have cleanProperty yet; before attaching object it's clean position is null
455 $this->attachObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
456 // if a relation is dirty (speaking the same object is removed and added again at a different position), all objects after this needs to be updated the sorting
457 $updateSortingOfFollowing = TRUE;
458 } elseif ($objectStorage->isRelationDirty($object) || $cleanProperty->getPosition($object) !== $objectStorage->getPosition($object)) {
459 $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
460 $updateSortingOfFollowing = TRUE;
461 } elseif ($updateSortingOfFollowing) {
462 if ($sortingPosition > $objectStorage->getPosition($object)) {
463 $this->updateRelationOfObjectToParentObject($object, $parentObject, $propertyName, $sortingPosition);
464 } else {
465 $sortingPosition = $objectStorage->getPosition($object);
466 }
467 }
468 $currentUids[] = $object->getUid();
469 }
470
471 if ($columnMap->getParentKeyFieldName() === NULL) {
472 $row[$columnMap->getColumnName()] = implode(',', $currentUids);
473 } else {
474 $row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
475 }
476 }
477
478 /**
479 * Returns the removed objects determined by a comparison of the clean property value
480 * with the actual property value.
481 *
482 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object
483 * @param string $propertyName
484 * @return array An array of removed objects
485 */
486 protected function getRemovedChildObjects(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $propertyName) {
487 $removedObjects = array();
488 $cleanPropertyValue = $object->_getCleanProperty($propertyName);
489 if (is_array($cleanPropertyValue) || $cleanPropertyValue instanceof \Iterator) {
490 $propertyValue = $object->_getProperty($propertyName);
491 foreach ($cleanPropertyValue as $containedObject) {
492 if (!$propertyValue->contains($containedObject)) {
493 $removedObjects[] = $containedObject;
494 }
495 }
496 }
497 return $removedObjects;
498 }
499
500 /**
501 * Updates the fields defining the relation between the object and the parent object.
502 *
503 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
504 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
505 * @param string $parentPropertyName
506 * @param integer $sortingPosition
507 * @return void
508 */
509 protected function attachObjectToParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 0) {
510 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
511 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
512 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
513 $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
514 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
515 $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
516 }
517 }
518
519 /**
520 * Updates the fields defining the relation between the object and the parent object.
521 *
522 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
523 * @param \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject
524 * @param string $parentPropertyName
525 * @param integer $sortingPosition
526 * @return void
527 */
528 protected function updateRelationOfObjectToParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0) {
529 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
530 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
531 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
532 $this->attachObjectToParentObjectRelationHasMany($object, $parentObject, $parentPropertyName, $sortingPosition);
533 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
534 $this->updateRelationInRelationTable($object, $parentObject, $parentPropertyName, $sortingPosition);
535 }
536 }
537
538 /**
539 * Updates fields defining the relation between the object and the parent object in relation has-many.
540 *
541 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
542 * @param \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject
543 * @param string $parentPropertyName
544 * @param integer $sortingPosition
545 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException
546 * @return void
547 */
548 protected function attachObjectToParentObjectRelationHasMany(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\AbstractEntity $parentObject, $parentPropertyName, $sortingPosition = 0) {
549 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
550 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
551 if ($parentColumnMap->getTypeOfRelation() !== \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
552 throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException(
553 'Parent column relation type is ' . $parentColumnMap->getTypeOfRelation() .
554 ' but should be ' . \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY,
555 1345368105
556 );
557 }
558 $row = array();
559 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
560 if ($parentKeyFieldName !== NULL) {
561 $row[$parentKeyFieldName] = $parentObject->getUid();
562 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
563 if ($parentTableFieldName !== NULL) {
564 $row[$parentTableFieldName] = $parentDataMap->getTableName();
565 }
566 $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
567 if (is_array($relationTableMatchFields)) {
568 $row = array_merge($relationTableMatchFields, $row);
569 }
570 }
571 $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
572 if (!empty($childSortByFieldName)) {
573 $row[$childSortByFieldName] = $sortingPosition;
574 }
575 if (!empty($row)) {
576 $this->updateObject($object, $row);
577 }
578 }
579
580 /**
581 * Updates the fields defining the relation between the object and the parent object.
582 *
583 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
584 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject
585 * @param string $parentPropertyName
586 * @return void
587 */
588 protected function detachObjectFromParentObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
589 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
590 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
591 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
592 $row = array();
593 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
594 if ($parentKeyFieldName !== NULL) {
595 $row[$parentKeyFieldName] = '';
596 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
597 if ($parentTableFieldName !== NULL) {
598 $row[$parentTableFieldName] = '';
599 }
600 $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
601 if (is_array($relationTableMatchFields) && count($relationTableMatchFields)) {
602 $row = array_merge(array_fill_keys(array_keys($relationTableMatchFields), ''), $row);
603 }
604 }
605 $childSortByFieldName = $parentColumnMap->getChildSortByFieldName();
606 if (!empty($childSortByFieldName)) {
607 $row[$childSortByFieldName] = 0;
608 }
609 if (count($row) > 0) {
610 $this->updateObject($object, $row);
611 }
612 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
613 $this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
614 }
615 }
616
617 /**
618 * Inserts an object in the storage backend
619 *
620 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be insterted in the storage
621 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parentobject.
622 * @param string $parentPropertyName
623 * @return void
624 */
625 protected function insertObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject = NULL, $parentPropertyName = '') {
626 if ($object instanceof \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject) {
627 $result = $this->getUidOfAlreadyPersistedValueObject($object);
628 if ($result !== FALSE) {
629 $object->_setProperty('uid', (int)$result);
630 return;
631 }
632 }
633 $dataMap = $this->dataMapper->getDataMap(get_class($object));
634 $row = array();
635 $this->addCommonFieldsToRow($object, $row);
636 if ($dataMap->getLanguageIdColumnName() !== NULL) {
637 $row[$dataMap->getLanguageIdColumnName()] = -1;
638 }
639 if ($parentObject !== NULL && $parentPropertyName) {
640 $parentColumnDataMap = $this->dataMapper->getDataMap(get_class($parentObject))->getColumnMap($parentPropertyName);
641 $relationTableMatchFields = $parentColumnDataMap->getRelationTableMatchFields();
642 if (is_array($relationTableMatchFields)) {
643 $row = array_merge($relationTableMatchFields, $row);
644 }
645 if ($parentColumnDataMap->getParentKeyFieldName() !== NULL) {
646 $row[$parentColumnDataMap->getParentKeyFieldName()] = (int)$parentObject->getUid();
647 }
648 }
649 $uid = $this->storageBackend->addRow($dataMap->getTableName(), $row);
650 $object->_setProperty('uid', (int)$uid);
651 if ((int)$uid >= 1) {
652 $this->emitAfterInsertObjectSignal($object);
653 }
654 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
655 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
656 $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $uid);
657 }
658 $this->session->registerObject($object, $uid);
659 }
660
661 /**
662 * Emits a signal after an object was added to the storage
663 *
664 * @param DomainObjectInterface $object
665 */
666 protected function emitAfterInsertObjectSignal(DomainObjectInterface $object) {
667 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterInsertObject', array($object));
668 }
669
670 /**
671 * Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
672 *
673 * @param \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object The object to be tested
674 * @return mixed The matching uid if an object was found, else FALSE
675 */
676 protected function getUidOfAlreadyPersistedValueObject(\TYPO3\CMS\Extbase\DomainObject\AbstractValueObject $object) {
677 return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
678 }
679
680 /**
681 * Inserts mm-relation into a relation table
682 *
683 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The related object
684 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
685 * @param string $propertyName The name of the parent object's property where the related objects are stored in
686 * @param integer $sortingPosition Defaults to NULL
687 * @return integer The uid of the inserted row
688 */
689 protected function insertRelationInRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, $sortingPosition = NULL) {
690 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
691 $columnMap = $dataMap->getColumnMap($propertyName);
692 $row = array(
693 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
694 $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
695 $columnMap->getChildSortByFieldName() => !is_null($sortingPosition) ? (int)$sortingPosition : 0
696 );
697 $relationTableName = $columnMap->getRelationTableName();
698 if ($columnMap->getRelationTablePageIdColumnName() !== NULL) {
699 $row[$columnMap->getRelationTablePageIdColumnName()] = $this->determineStoragePageIdForNewRecord();
700 }
701 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
702 if (is_array($relationTableMatchFields)) {
703 $row = array_merge($relationTableMatchFields, $row);
704 }
705 $relationTableInsertFields = $columnMap->getRelationTableInsertFields();
706 if (is_array($relationTableInsertFields)) {
707 $row = array_merge($relationTableInsertFields, $row);
708 }
709 $res = $this->storageBackend->addRow($relationTableName, $row, TRUE);
710 return $res;
711 }
712
713 /**
714 * Inserts mm-relation into a relation table
715 *
716 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The related object
717 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
718 * @param string $propertyName The name of the parent object's property where the related objects are stored in
719 * @param integer $sortingPosition Defaults to NULL
720 * @return integer The uid of the inserted row
721 */
722 protected function updateRelationInRelationTable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $propertyName, $sortingPosition = 0) {
723 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
724 $columnMap = $dataMap->getColumnMap($propertyName);
725 $row = array(
726 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
727 $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
728 $columnMap->getChildSortByFieldName() => (int)$sortingPosition
729 );
730 $relationTableName = $columnMap->getRelationTableName();
731 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
732 if (is_array($relationTableMatchFields)) {
733 $row = array_merge($relationTableMatchFields, $row);
734 }
735 $res = $this->storageBackend->updateRelationTableRow(
736 $relationTableName,
737 $row);
738 return $res;
739 }
740
741 /**
742 * Delete all mm-relations of a parent from a relation table
743 *
744 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
745 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
746 * @return boolean
747 */
748 protected function deleteAllRelationsFromRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
749 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
750 $columnMap = $dataMap->getColumnMap($parentPropertyName);
751 $relationTableName = $columnMap->getRelationTableName();
752 $relationMatchFields = array(
753 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
754 );
755 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
756 if (is_array($relationTableMatchFields)) {
757 $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
758 }
759 $res = $this->storageBackend->removeRow($relationTableName, $relationMatchFields, FALSE);
760 return $res;
761 }
762
763 /**
764 * Delete an mm-relation from a relation table
765 *
766 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $relatedObject The related object
767 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
768 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
769 * @return boolean
770 */
771 protected function deleteRelationFromRelationtable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $relatedObject, \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
772 $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
773 $columnMap = $dataMap->getColumnMap($parentPropertyName);
774 $relationTableName = $columnMap->getRelationTableName();
775 $relationMatchFields = array(
776 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
777 $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid()
778 );
779 $relationTableMatchFields = $columnMap->getRelationTableMatchFields();
780 if (is_array($relationTableMatchFields)) {
781 $relationMatchFields = array_merge($relationTableMatchFields, $relationMatchFields);
782 }
783 $res = $this->storageBackend->removeRow($relationTableName, $relationMatchFields, FALSE);
784 return $res;
785 }
786
787 /**
788 * Fetches maximal value currently used for sorting field in parent table
789 *
790 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject The parent object
791 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
792 * @throws \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException
793 * @return mixed the max value
794 */
795 protected function fetchMaxSortingFromParentTable(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $parentObject, $parentPropertyName) {
796 $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
797 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
798 if ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
799 $tableName = $parentColumnMap->getChildTableName();
800 $sortByFieldName = $parentColumnMap->getChildSortByFieldName();
801
802 if (empty($sortByFieldName)) {
803 return FALSE;
804 }
805 $matchFields = array();
806 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
807 if ($parentKeyFieldName !== NULL) {
808 $matchFields[$parentKeyFieldName] = $parentObject->getUid();
809 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
810 if ($parentTableFieldName !== NULL) {
811 $matchFields[$parentTableFieldName] = $parentDataMap->getTableName();
812 }
813 }
814
815 if (empty($matchFields)) {
816 return FALSE;
817 }
818 } elseif ($parentColumnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
819 $tableName = $parentColumnMap->getRelationTableName();
820 $sortByFieldName = $parentColumnMap->getChildSortByFieldName();
821
822 $matchFields = array(
823 $parentColumnMap->getParentKeyFieldName() => (int)$parentObject->getUid()
824 );
825
826 $relationTableMatchFields = $parentColumnMap->getRelationTableMatchFields();
827 if (is_array($relationTableMatchFields)) {
828 $matchFields = array_merge($relationTableMatchFields, $matchFields);
829 }
830 } else {
831 throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalRelationTypeException('Unexpected parent column relation type:' . $parentColumnMap->getTypeOfRelation(), 1345368106);
832 }
833
834 $result = $this->storageBackend->getMaxValueFromTable(
835 $tableName,
836 $matchFields,
837 $sortByFieldName);
838 return $result;
839 }
840
841 /**
842 * Updates a given object in the storage
843 *
844 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be updated
845 * @param array $row Row to be stored
846 * @return boolean
847 */
848 protected function updateObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array $row) {
849 $dataMap = $this->dataMapper->getDataMap(get_class($object));
850 $this->addCommonFieldsToRow($object, $row);
851 $row['uid'] = $object->getUid();
852 if ($dataMap->getLanguageIdColumnName() !== NULL) {
853 $row[$dataMap->getLanguageIdColumnName()] = $object->_getProperty('_languageUid');
854 if ($object->_getProperty('_localizedUid') !== NULL) {
855 $row['uid'] = $object->_getProperty('_localizedUid');
856 }
857 }
858 $res = $this->storageBackend->updateRow($dataMap->getTableName(), $row);
859 if ($res === TRUE) {
860 $this->emitAfterUpdateObjectSignal($object);
861 }
862 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
863 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
864 $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $row['uid']);
865 }
866 return $res;
867 }
868
869 /**
870 * Emits a signal after an object was updated in storage
871 *
872 * @param DomainObjectInterface $object
873 */
874 protected function emitAfterUpdateObjectSignal(DomainObjectInterface $object) {
875 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterUpdateObject', array($object));
876 }
877
878 /**
879 * Adds common databse fields to a row
880 *
881 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
882 * @param array &$row
883 * @return void
884 */
885 protected function addCommonFieldsToRow(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array &$row) {
886 $dataMap = $this->dataMapper->getDataMap(get_class($object));
887 $this->addCommonDateFieldsToRow($object, $row);
888 if ($dataMap->getRecordTypeColumnName() !== NULL && $dataMap->getRecordType() !== NULL) {
889 $row[$dataMap->getRecordTypeColumnName()] = $dataMap->getRecordType();
890 }
891 if ($object->_isNew() && !isset($row['pid'])) {
892 $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
893 }
894 }
895
896 /**
897 * Adjustes the common date fields of the given row to the current time
898 *
899 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
900 * @param array &$row The row to be updated
901 * @return void
902 */
903 protected function addCommonDateFieldsToRow(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, array &$row) {
904 $dataMap = $this->dataMapper->getDataMap(get_class($object));
905 if ($object->_isNew() && $dataMap->getCreationDateColumnName() !== NULL) {
906 $row[$dataMap->getCreationDateColumnName()] = $GLOBALS['EXEC_TIME'];
907 }
908 if ($dataMap->getModificationDateColumnName() !== NULL) {
909 $row[$dataMap->getModificationDateColumnName()] = $GLOBALS['EXEC_TIME'];
910 }
911 }
912
913 /**
914 * Iterate over deleted aggregate root objects and process them
915 *
916 * @return void
917 */
918 protected function processDeletedObjects() {
919 foreach ($this->deletedEntities as $entity) {
920 if ($this->session->hasObject($entity)) {
921 $this->removeEntity($entity);
922 $this->session->unregisterReconstitutedEntity($entity);
923 $this->session->unregisterObject($entity);
924 }
925 }
926 $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
927 }
928
929 /**
930 * Deletes an object
931 *
932 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to be removed from the storage
933 * @param boolean $markAsDeleted Whether to just flag the row deleted (default) or really delete it
934 * @return void
935 */
936 protected function removeEntity(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $markAsDeleted = TRUE) {
937 $dataMap = $this->dataMapper->getDataMap(get_class($object));
938 $tableName = $dataMap->getTableName();
939 if ($markAsDeleted === TRUE && $dataMap->getDeletedFlagColumnName() !== NULL) {
940 $deletedColumnName = $dataMap->getDeletedFlagColumnName();
941 $row = array(
942 'uid' => $object->getUid(),
943 $deletedColumnName => 1
944 );
945 $this->addCommonDateFieldsToRow($object, $row);
946 $res = $this->storageBackend->updateRow($tableName, $row);
947 } else {
948 $res = $this->storageBackend->removeRow($tableName, array('uid' => $object->getUid()));
949 }
950 if ($res === TRUE) {
951 $this->emitAfterRemoveObjectSignal($object);
952 }
953 $this->removeRelatedObjects($object);
954 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
955 if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
956 $this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
957 }
958 }
959
960 /**
961 * Emits a signal after an object was removed from storage
962 *
963 * @param DomainObjectInterface $object
964 */
965 protected function emitAfterRemoveObjectSignal(DomainObjectInterface $object) {
966 $this->signalSlotDispatcher->dispatch(__CLASS__, 'afterRemoveObject', array($object));
967 }
968
969 /**
970 * Remove related objects
971 *
972 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object The object to scanned for related objects
973 * @return void
974 */
975 protected function removeRelatedObjects(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) {
976 $className = get_class($object);
977 $dataMap = $this->dataMapper->getDataMap($className);
978 $classSchema = $this->reflectionService->getClassSchema($className);
979 $properties = $object->_getProperties();
980 foreach ($properties as $propertyName => $propertyValue) {
981 $columnMap = $dataMap->getColumnMap($propertyName);
982 $propertyMetaData = $classSchema->getProperty($propertyName);
983 if ($propertyMetaData['cascade'] === 'remove') {
984 if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
985 foreach ($propertyValue as $containedObject) {
986 $this->removeEntity($containedObject);
987 }
988 } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
989 $this->removeEntity($propertyValue);
990 }
991 }
992 }
993 }
994
995 /**
996 * Determine the storage page ID for a given NEW record
997 *
998 * This does the following:
999 * - If the domain object has an accessible property 'pid' (i.e. through a getPid() method), that is used to store the record.
1000 * - If there is a TypoScript configuration "classes.CLASSNAME.newRecordStoragePid", that is used to store new records.
1001 * - If there is no such TypoScript configuration, it uses the first value of The "storagePid" taken for reading records.
1002 *
1003 * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
1004 * @return integer the storage Page ID where the object should be stored
1005 */
1006 protected function determineStoragePageIdForNewRecord(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object = NULL) {
1007 $frameworkConfiguration = $this->configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK);
1008 if ($object !== NULL) {
1009 if (\TYPO3\CMS\Extbase\Reflection\ObjectAccess::isPropertyGettable($object, 'pid')) {
1010 $pid = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getProperty($object, 'pid');
1011 if (isset($pid)) {
1012 return (int)$pid;
1013 }
1014 }
1015 $className = get_class($object);
1016 if (isset($frameworkConfiguration['persistence']['classes'][$className]) && !empty($frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'])) {
1017 return (int)$frameworkConfiguration['persistence']['classes'][$className]['newRecordStoragePid'];
1018 }
1019 }
1020 $storagePidList = \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $frameworkConfiguration['persistence']['storagePid']);
1021 return (int)$storagePidList[0];
1022 }
1023
1024 /**
1025 * Returns a plain value, i.e. objects are flattened out if possible.
1026 *
1027 * @param mixed $input
1028 * @param \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap $columnMap
1029 * @return mixed
1030 */
1031 protected function getPlainValue($input, $columnMap = NULL) {
1032 if ($input instanceof \DateTime) {
1033 if (!is_null($columnMap) && !is_null($columnMap->getDateTimeStorageFormat())) {
1034 if ($columnMap->getDateTimeStorageFormat() == 'datetime') {
1035 return $input->format('Y-m-d H:i:s');
1036 }
1037 if ($columnMap->getDateTimeStorageFormat() == 'date') {
1038 return $input->format('Y-m-d');
1039 }
1040 } else {
1041 return $input->format('U');
1042 }
1043 } elseif ($input instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
1044 return $input->getUid();
1045 } elseif ($input instanceof \TYPO3\CMS\Core\Type\TypeInterface) {
1046 return (string) $input;
1047 } elseif (is_bool($input)) {
1048 return $input === TRUE ? 1 : 0;
1049 } else {
1050 return $input;
1051 }
1052 return NULL;
1053 }
1054 }