2 /***************************************************************
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
8 * This class is a backport of the corresponding class of FLOW3.
9 * All credits go to the v5 team.
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.
17 * The GNU General Public License can be found at
18 * http://www.gnu.org/copyleft/gpl.html.
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.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
29 * A persistence backend. This backend maps objects to the relational model of the storage backend.
30 * It persists all added, removed and changed objects.
33 * @subpackage Persistence
34 * @version $Id: Backend.php 2183 2009-04-24 14:28:37Z k-fish $
36 class Tx_Extbase_Persistence_Backend
implements Tx_Extbase_Persistence_BackendInterface
, t3lib_Singleton
{
39 * @var Tx_Extbase_Persistence_Session
44 * @var Tx_Extbase_Persistence_ObjectStorage
46 protected $aggregateRootObjects;
49 * @var Tx_Extbase_Persistence_IdentityMap
51 protected $identityMap;
54 * @var Tx_Extbase_Reflection_Service
56 protected $reflectionService;
59 * @var Tx_Extbase_Persistence_QueryFactoryInterface
61 protected $queryFactory;
64 * @var Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
66 protected $QOMFactory;
69 * @var Tx_Extbase_Persistence_ValueFactoryInterface
71 protected $valueFactory;
74 * @var Tx_Extbase_Persistence_Storage_BackendInterface
76 protected $storageBackend;
79 * @var Tx_Extbase_Persistence_DataMapper
81 protected $dataMapper;
84 * The TYPO3 reference index object
88 protected $referenceIndex;
93 protected $extbaseSettings;
96 * Constructs the backend
98 * @param Tx_Extbase_Persistence_Session $session The persistence session used to persist data
100 public function __construct(Tx_Extbase_Persistence_Session
$session, Tx_Extbase_Persistence_Storage_BackendInterface
$storageBackend) {
101 $this->session
= $session;
102 $this->storageBackend
= $storageBackend;
103 $this->extbaseSettings
= Tx_Extbase_Dispatcher
::getExtbaseFrameworkConfiguration();
104 if ($this->extbaseSettings
['persistence']['updateReferenceIndex'] === '1') {
105 $this->referenceIndex
= t3lib_div
::makeInstance('t3lib_refindex');
107 $this->aggregateRootObjects
= new Tx_Extbase_Persistence_ObjectStorage();
111 * Injects the DataMapper to map nodes to objects
113 * @param Tx_Extbase_Persistence_Mapper_DataMapper $dataMapper
116 public function injectDataMapper(Tx_Extbase_Persistence_Mapper_DataMapper
$dataMapper) {
117 $this->dataMapper
= $dataMapper;
121 * Injects the identity map
123 * @param Tx_Extbase_Persistence_IdentityMap $identityMap
126 public function injectIdentityMap(Tx_Extbase_Persistence_IdentityMap
$identityMap) {
127 $this->identityMap
= $identityMap;
131 * Injects the Reflection Service
133 * @param Tx_Extbase_Reflection_Service
136 public function injectReflectionService(Tx_Extbase_Reflection_Service
$reflectionService) {
137 $this->reflectionService
= $reflectionService;
141 * Injects the QueryFactory
143 * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
146 public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface
$queryFactory) {
147 $this->queryFactory
= $queryFactory;
151 * Injects the QueryObjectModelFactory
153 * @param Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface $dataMapper
156 public function injectQOMFactory(Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
$QOMFactory) {
157 $this->QOMFactory
= $QOMFactory;
161 * Injects the ValueFactory
163 * @param Tx_Extbase_Persistence_ValueFactoryInterface $valueFactory
166 public function injectValueFactory(Tx_Extbase_Persistence_ValueFactoryInterface
$valueFactory) {
167 $this->valueFactory
= $valueFactory;
171 * Returns the repository session
173 * @return Tx_Extbase_Persistence_Session
175 public function getSession() {
176 return $this->session
;
180 * Returns the Data Mapper
182 * @return Tx_Extbase_Persistence_Mapper_DataMapper
184 public function getDataMapper() {
185 return $this->dataMapper
;
189 * Returns the current QOM factory
191 * @return Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
193 public function getQOMFactory() {
194 return $this->QOMFactory
;
198 * Returns the current value factory
200 * @return Tx_Extbase_Persistence_ValueFactoryInterface
202 public function getValueFactory() {
203 return $this->valueFactory
;
207 * Returns the current identityMap
209 * @return Tx_Extbase_Persistence_IdentityMap
211 public function getIdentityMap() {
212 return $this->identityMap
;
216 * Returns the (internal) identifier for the object, if it is known to the
217 * backend. Otherwise NULL is returned.
219 * @param object $object
220 * @return string The identifier for the object if it is known, or NULL
222 public function getIdentifierByObject($object) {
223 if ($this->identityMap
->hasObject($object)) {
224 return $this->identityMap
->getIdentifierByObject($object);
231 * Returns the object with the (internal) identifier, if it is known to the
232 * backend. Otherwise NULL is returned.
234 * @param string $identifier
235 * @param string $className
236 * @return object The object for the identifier if it is known, or NULL
238 public function getObjectByIdentifier($identifier, $className) {
239 if ($this->identityMap
->hasIdentifier($identifier, $className)) {
240 return $this->identityMap
->getObjectByIdentifier($identifier, $className);
242 $query = $this->queryFactory
->create($className);
243 $result = $query->matching($query->withUid($identifier))->execute();
245 if (count($result) > 0) {
246 $object = current($result);
253 * Checks if the given object has ever been persisted.
255 * @param object $object The object to check
256 * @return boolean TRUE if the object is new, FALSE if the object exists in the repository
258 public function isNewObject($object) {
259 return ($this->getIdentifierByObject($object) === NULL);
263 * Replaces the given object by the second object.
265 * This method will unregister the existing object at the identity map and
266 * register the new object instead. The existing object must therefore
267 * already be registered at the identity map which is the case for all
268 * reconstituted objects.
270 * The new object will be identified by the uid which formerly belonged
271 * to the existing object. The existing object looses its uid.
273 * @param object $existingObject The existing object
274 * @param object $newObject The new object
277 public function replaceObject($existingObject, $newObject) {
278 $existingUid = $this->getIdentifierByObject($existingObject);
279 if ($existingUid === NULL) throw new Tx_Extbase_Persistence_Exception_UnknownObject('The given object is unknown to this persistence backend.', 1238070163);
281 $this->identityMap
->unregisterObject($existingObject);
282 $this->identityMap
->registerObject($newObject, $existingUid);
286 * Sets the aggregate root objects
288 * @param Tx_Extbase_Persistence_ObjectStorage $objects
291 public function setAggregateRootObjects(Tx_Extbase_Persistence_ObjectStorage
$objects) {
292 $this->aggregateRootObjects
= $objects;
296 * Sets the deleted objects
298 * @param Tx_Extbase_Persistence_ObjectStorage $objects
301 public function setDeletedObjects(Tx_Extbase_Persistence_ObjectStorage
$objects) {
302 $this->deletedObjects
= $objects;
306 * Commits the current persistence session.
310 public function commit() {
311 $this->persistObjects();
312 $this->processDeletedObjects();
316 * Traverse and persist all aggregate roots and their object graph.
320 protected function persistObjects() {
321 foreach ($this->aggregateRootObjects
as $object) {
322 // if (!$this->identityMap->hasObject($object)) { // TODO Must be enabled to allow other identity properties than $uid
323 if ($object->_isNew()) {
324 $this->insertObject($object);
328 foreach ($this->aggregateRootObjects
as $object) {
329 $this->persistObject($object);
334 * Persists an object (instert, update) and its related objects (instert, update, delete).
336 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be inserted
337 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
338 * @param string $parentPropertyName The name of the property the object is stored in
341 protected function persistObject(Tx_Extbase_DomainObject_DomainObjectInterface
$object) {
344 $className = get_class($object);
345 $dataMap = $this->dataMapper
->getDataMap($className);
346 $classSchema = $this->reflectionService
->getClassSchema($className);
348 $properties = $object->_getProperties();
349 foreach ($properties as $propertyName => $propertyValue) {
350 if (!$dataMap->isPersistableProperty($propertyName)) continue;
351 if (($propertyValue instanceof Tx_Extbase_Persistence_LazyLoadingProxy
) ||
((get_class($propertyValue) === 'Tx_Extbase_Persistence_LazyObjectStorage') && ($propertyValue->isInitialized() === FALSE))) {
355 $columnMap = $dataMap->getColumnMap($propertyName);
356 $propertyMetaData = $classSchema->getProperty($propertyName);
357 $propertyType = $propertyMetaData['type'];
358 // FIXME enable property-type check
359 // $this->checkPropertyType($propertyType, $propertyValue);
360 if (($propertyValue !== NULL) && $propertyType === 'Tx_Extbase_Persistence_ObjectStorage') {
361 if ($object->_isNew() ||
$object->_isDirty($propertyName)) {
362 $this->persistObjectStorage($propertyValue, $object, $propertyName, $queue, $row);
364 foreach ($propertyValue as $containedObject) {
365 $queue[] = $containedObject;
368 } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface
) {
369 if ($object->_isDirty($propertyName)) {
370 if ($propertyValue->_isNew()) {
371 if ($propertyValue instanceof Tx_Extbase_DomainObject_AbstractEntity
) {
372 $this->insertObject($propertyValue, $object, $propertyName);
373 $queue[] = $propertyValue;
375 $this->persistValueObject($propertyValue, $object, $propertyName);
378 $row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
380 } elseif ($object instanceof Tx_Extbase_DomainObject_AbstractValueObject ||
($object->_isNew() ||
$object->_isDirty($propertyName))) {
381 $row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
385 if (count($row) > 0) {
386 $this->updateObject($object, $row);
389 if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity
) {
390 $object->_memorizeCleanState();
393 foreach ($queue as $object) {
394 $this->persistObject($object);
400 * Checks a value given against the expected type. If not matching, an
401 * UnexpectedTypeException is thrown. NULL is always considered valid.
403 * @param string $expectedType The expected type
404 * @param mixed $value The value to check
406 * @throws Tx_Extbase_Persistence_Exception_UnexpectedType
408 protected function checkPropertyType($expectedType, $value) {
409 if ($value === NULL) {
413 if (is_object($value)) {
414 if (!($value instanceof $expectedType)) {
415 throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Expected property of type ' . $expectedType . ', but got ' . get_class($value), 1244465558);
417 } elseif ($expectedType !== gettype($value)) {
418 throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Expected property of type ' . $expectedType . ', but got ' . gettype($value), 1244465558);
423 * Persists the given value object.
427 protected function persistValueObject(Tx_Extbase_DomainObject_AbstractValueObject
$object, Tx_Extbase_DomainObject_DomainObjectInterface
$parentObject, $parentPropertyName, $sortingPosition = 1) {
428 $result = $this->getUidOfAlreadyPersistedValueObject($object);
429 if ($result !== FALSE) {
430 $object->_setProperty('uid', (int)$result);
431 if($this->dataMapper
->getDataMap(get_class($parentObject))->getColumnMap($parentPropertyName)->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_AND_BELONGS_TO_MANY
) {
432 $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
434 } elseif ($object->_isNew()) {
436 $className = get_class($object);
437 $dataMap = $this->dataMapper
->getDataMap($className);
438 $classSchema = $this->reflectionService
->getClassSchema($className);
440 $properties = $object->_getProperties();
441 foreach ($properties as $propertyName => $propertyValue) {
442 if (!$dataMap->isPersistableProperty($propertyName)) continue;
443 if (($propertyValue instanceof Tx_Extbase_Persistence_LazyLoadingProxy
) ||
((get_class($propertyValue) === 'Tx_Extbase_Persistence_LazyObjectStorage') && ($propertyValue->isInitialized() === FALSE))) {
447 $columnMap = $dataMap->getColumnMap($propertyName);
448 $propertyMetaData = $classSchema->getProperty($propertyName);
449 $propertyType = $propertyMetaData['type'];
450 // FIXME enable property-type check
451 // $this->checkPropertyType($propertyType, $propertyValue);
452 $row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
454 $this->insertObject($object, $parentObject, $parentPropertyName, $sortingPosition, $row);
459 * Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
461 * @param Tx_Extbase_DomainObject_AbstractValueObject $object The object to be tested
463 protected function getUidOfAlreadyPersistedValueObject(Tx_Extbase_DomainObject_AbstractValueObject
$object) {
464 return $this->storageBackend
->getUidOfAlreadyPersistedValueObject($object);
468 * Persists a relation. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
469 * gets persisted immediately. Objects which were removed from the property were detached from the parent object. They will not be
470 * deleted by default. You have to annotate the property with "@cascade remove" if you want them to be deleted as well.
472 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object
473 * @param string $propertyName The name of the property the related objects are stored in
474 * @param mixed $propertyValue The property value
477 protected function persistObjectStorage(Tx_Extbase_Persistence_ObjectStorage
$objectStorage, Tx_Extbase_DomainObject_DomainObjectInterface
$parentObject, $propertyName, array &$queue, array &$row) {
478 $className = get_class($parentObject);
479 $columnMap = $this->dataMapper
->getDataMap($className)->getColumnMap($propertyName);
480 $columnName = $columnMap->getColumnName();
481 $propertyMetaData = $this->reflectionService
->getClassSchema($className)->getProperty($propertyName);
483 $updateParent = FALSE;
484 foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
485 if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_MANY
&& $propertyMetaData['cascade'] === 'remove') {
486 $this->removeObject($removedObject);
488 $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
490 $updateParent = TRUE;
493 $sortingPosition = 1;
494 foreach ($objectStorage as $object) {
495 if ($object->_isNew()) {
496 if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity
) {
497 $this->insertObject($object, $parentObject, $propertyName, $sortingPosition);
500 $this->persistValueObject($object, $parentObject, $propertyName, $sortingPosition);
502 $updateParent = TRUE;
507 if ($updateParent === TRUE && $columnMap->getParentKeyFieldName() !== NULL) {
508 $row[$columnMap->getColumnName()] = $this->dataMapper
->countRelated($parentObject, $propertyName);
513 * Returns the removed objects determined by a comparison of the clean property value
514 * with the actual property value.
516 * @param Tx_Extbase_DomainObject_AbstractEntity $object The object
517 * @param string $parentPropertyName The name of the property
518 * @return array An array of removed objects
520 protected function getRemovedChildObjects(Tx_Extbase_DomainObject_AbstractEntity
$object, $propertyName) {
521 $removedObjects = array();
522 $cleanPropertyValue = $object->_getCleanProperty($propertyName);
523 $propertyValue = $object->_getProperty($propertyName);
524 if ($cleanPropertyValue instanceof Tx_Extbase_Persistence_ObjectStorage
) {
525 $cleanPropertyValue = $cleanPropertyValue->toArray();
527 if ($propertyValue instanceof Tx_Extbase_Persistence_ObjectStorage
) {
528 $propertyValue = $propertyValue->toArray();
530 if ($cleanPropertyValue instanceof Iterator
) {
531 foreach ($cleanPropertyValue as $hash => $item) {
532 if (!array_key_exists($hash, $propertyValue)) {
533 $removedObjects[] = $item;
537 return $removedObjects;
541 * Updates the fields defining the relation between the object and the parent object.
543 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
544 * @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
545 * @param string $parentPropertyName
548 protected function detachObjectFromParentObject(Tx_Extbase_DomainObject_DomainObjectInterface
$object, Tx_Extbase_DomainObject_AbstractEntity
$parentObject, $parentPropertyName) {
549 $parentDataMap = $this->dataMapper
->getDataMap(get_class($parentObject));
550 $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
551 if ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_MANY
) {
553 $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
554 if ($parentKeyFieldName !== NULL) {
555 $row[$parentKeyFieldName] = '';
556 $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
557 if ($parentTableFieldName !== NULL) {
558 $row[$parentTableFieldName] = '';
561 if (count($row) > 0) {
562 $this->updateObject($object, $row);
564 } elseif ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_AND_BELONGS_TO_MANY
) {
565 $this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
570 * Inserts an object in the storage
572 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
573 * @param array $row The tuple to be inserted
574 * @param Tx_Extbase_DomainObject_AbstractEntity $parentObject The parent object (if any)
575 * @param string $parentPropertyName The name of the property
577 protected function insertObject(Tx_Extbase_DomainObject_DomainObjectInterface
$object, Tx_Extbase_DomainObject_AbstractEntity
$parentObject = NULL, $propertyName = NULL, $sortingPosition = NULL, array $row = array()) {
578 $tableName = $this->dataMapper
->getDataMap(get_class($object))->getTableName();
579 $this->addCommonFieldsToRow($object, $row);
580 if ($parentObject !== NULL) {
581 $parentColumnMap = $this->dataMapper
->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
582 if ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_MANY
&& $parentColumnMap->getParentKeyFieldName() !== NULL) {
583 $row[$parentColumnMap->getParentKeyFieldName()] = $parentObject->getUid();
586 if ($object->_isNew()) {
587 $uid = $this->storageBackend
->addRow(
591 $object->_setProperty('uid', (int)$uid);
593 if ($parentObject !== NULL) {
594 if($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_AND_BELONGS_TO_MANY
) {
595 $this->insertRelationInRelationtable($object, $parentObject, $propertyName, $sortingPosition);
598 if ($this->extbaseSettings
['persistence']['updateReferenceIndex'] === '1') {
599 $this->referenceIndex
->updateRefIndexTable($tableName, $uid);
601 $this->identityMap
->registerObject($object, $uid);
605 * Inserts mm-relation into a relation table
607 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The related object
608 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
609 * @param string $propertyName The name of the parent object's property where the related objects are stored in
612 protected function insertRelationInRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface
$object, Tx_Extbase_DomainObject_DomainObjectInterface
$parentObject, $propertyName, $sortingPosition = NULL) {
613 $dataMap = $this->dataMapper
->getDataMap(get_class($parentObject));
614 $columnMap = $dataMap->getColumnMap($propertyName);
616 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
617 $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
618 $columnMap->getChildSortByFieldName() => !is_null($sortingPosition) ?
(int)$sortingPosition : 0
620 $relationTableName = $columnMap->getRelationTableName();
621 // FIXME Reenable support for tablenames
622 // $childTableName = $columnMap->getChildTableName();
623 // if (isset($childTableName)) {
624 // $row['tablenames'] = $childTableName;
626 $res = $this->storageBackend
->addRow(
634 * Delete an mm-relation from a relation table
636 * @param Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject The related object
637 * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
638 * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
641 protected function deleteRelationFromRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface
$relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface
$parentObject, $parentPropertyName) {
642 $dataMap = $this->dataMapper
->getDataMap(get_class($parentObject));
643 $columnMap = $dataMap->getColumnMap($parentPropertyName);
644 $relationTableName = $columnMap->getRelationTableName();
645 $res = $this->storageBackend
->removeRow(
648 $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
649 $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid(),
656 * Updates a given object in the storage
658 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
659 * @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
660 * @param string|NULL $parentPropertyName The name of the property
661 * @param array $row The $row
663 protected function updateObject(Tx_Extbase_DomainObject_DomainObjectInterface
$object, array &$row) {
664 $tableName = $this->dataMapper
->getDataMap(get_class($object))->getTableName();
665 $this->addCommonFieldsToRow($object, $row);
666 $uid = $object->getUid();
668 $res = $this->storageBackend
->updateRow(
672 if ($this->extbaseSettings
['persistence']['updateReferenceIndex'] === '1') {
673 $this->referenceIndex
->updateRefIndexTable($tableName, $uid);
675 if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity
) {
676 $object->_memorizeCleanState();
682 * Returns a table row to be inserted or updated in the database
684 * @param Tx_Extbase_Persistence_Mapper_DataMap $dataMap The appropriate data map representing a database table
685 * @param array $properties The properties of the object
686 * @return array A single row to be inserted in the database
688 protected function addCommonFieldsToRow(Tx_Extbase_DomainObject_DomainObjectInterface
$object, array &$row) {
689 $className = get_class($object);
690 $dataMap = $this->dataMapper
->getDataMap($className);
691 if ($dataMap->hasCreationDateColumn() && $object->_isNew()) {
692 $row[$dataMap->getCreationDateColumnName()] = $GLOBALS['EXEC_TIME'];
694 if ($dataMap->hasTimestampColumn()) {
695 $row[$dataMap->getTimestampColumnName()] = $GLOBALS['EXEC_TIME'];
697 if ($object->_isNew() && $dataMap->hasPidColumn() && !isset($row['pid'])) {
698 $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
703 * Iterate over deleted aggregate root objects and process them
707 protected function processDeletedObjects() {
708 foreach ($this->deletedObjects
as $object) {
709 $this->removeObject($object);
710 $this->identityMap
->unregisterObject($object);
712 $this->deletedObjects
= new Tx_Extbase_Persistence_ObjectStorage();
718 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
719 * @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
720 * @param string|NULL $parentPropertyName The name of the property
721 * @param bool $markAsDeleted Shold we only mark the row as deleted instead of deleting (TRUE by default)?
724 protected function removeObject(Tx_Extbase_DomainObject_DomainObjectInterface
$object, $markAsDeleted = TRUE) {
725 $dataMap = $this->dataMapper
->getDataMap(get_class($object));
726 $tableName = $dataMap->getTableName();
727 if (($markAsDeleted === TRUE) && $dataMap->hasDeletedColumn()) {
728 $deletedColumnName = $dataMap->getDeletedColumnName();
729 $res = $this->storageBackend
->updateRow(
732 'uid' => $object->getUid(),
733 $deletedColumnName => 1
737 $res = $this->storageBackend
->removeRow(
739 array('uid' => $object->getUid())
742 $this->removeRelatedObjects($object);
743 if ($this->extbaseSettings
['persistence']['updateReferenceIndex'] === '1') {
744 $this->referenceIndex
->updateRefIndexTable($tableName, $object->getUid());
749 * Remove related objects
751 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to scanned for related objects
754 protected function removeRelatedObjects(Tx_Extbase_DomainObject_DomainObjectInterface
$object) {
755 $className = get_class($object);
756 $dataMap = $this->dataMapper
->getDataMap($className);
757 $classSchema = $this->reflectionService
->getClassSchema($className);
759 $properties = $object->_getProperties();
760 foreach ($properties as $propertyName => $propertyValue) {
761 $columnMap = $dataMap->getColumnMap($propertyName);
762 $propertyMetaData = $classSchema->getProperty($propertyName);
763 if ($propertyMetaData['cascade'] === 'remove') {
764 if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap
::RELATION_HAS_MANY
) {
765 foreach ($propertyValue as $containedObject) {
766 $this->removeObject($containedObject);
768 } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface
) {
769 $this->removeObject($propertyValue);
776 * Delegates the call to the Data Map.
777 * Returns TRUE if the property is persistable (configured in $TCA)
779 * @param string $className The property name
780 * @param string $propertyName The property name
781 * @return boolean TRUE if the property is persistable (configured in $TCA)
783 public function isPersistableProperty($className, $propertyName) {
784 $dataMap = $this->dataMapper
->getDataMap($className);
785 return $dataMap->isPersistableProperty($propertyName);
789 * Determine the storage page ID for a given NEW record
791 * This does the following:
792 * - If there is a TypoScript configuration "classes.CLASSNAME.newRecordStoragePid", that is used to store new records.
793 * - If there is no such TypoScript configuration, it uses the first value of The "storagePid" taken for reading records.
795 * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
796 * @return int the storage Page ID where the object should be stored
798 protected function determineStoragePageIdForNewRecord(Tx_Extbase_DomainObject_DomainObjectInterface
$object) {
799 $className = get_class($object);
800 $extbaseSettings = Tx_Extbase_Dispatcher
::getExtbaseFrameworkConfiguration();
802 if (isset($extbaseSettings['persistence']['classes'][$className]) && !empty($extbaseSettings['persistence']['classes'][$className]['newRecordStoragePid'])) {
803 return (int)$extbaseSettings['persistence']['classes'][$className]['newRecordStoragePid'];
805 $storagePidList = t3lib_div
::intExplode(',', $extbaseSettings['persistence']['storagePid']);
806 return (int) $storagePidList[0];