* (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
* All rights reserved
*
-* This class is a backport of the corresponding class of FLOW3.
+* This class is a backport of the corresponding class of FLOW3.
* All credits go to the v5 team.
*
* This script is part of the TYPO3 project. The TYPO3 project is
**/
protected $identityMap;
+ /**
+ * @var Tx_Extbase_Reflection_Service
+ */
+ protected $reflectionService;
+
+ /**
+ * @var Tx_Extbase_Persistence_QueryFactoryInterface
+ */
+ protected $queryFactory;
+
/**
* @var Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
*/
protected $referenceIndex;
/**
- * Storage page ID used to store records. Set by the Dispatcher.
- * @var integer
- */
- protected $storagePageId;
-
+ * @var array
+ **/
+ protected $extbaseSettings;
+
/**
* Constructs the backend
*
public function __construct(Tx_Extbase_Persistence_Session $session, Tx_Extbase_Persistence_Storage_BackendInterface $storageBackend) {
$this->session = $session;
$this->storageBackend = $storageBackend;
- $this->referenceIndex = t3lib_div::makeInstance('t3lib_refindex');
+ $this->extbaseSettings = Tx_Extbase_Dispatcher::getExtbaseFrameworkConfiguration();
+ if ($this->extbaseSettings['persistence']['updateReferenceIndex'] === '1') {
+ $this->referenceIndex = t3lib_div::makeInstance('t3lib_refindex');
+ }
$this->aggregateRootObjects = new Tx_Extbase_Persistence_ObjectStorage();
- $this->persistenceBackend = $GLOBALS['TYPO3_DB']; // FIXME This is just an intermediate solution
- $extbaseSettings = Tx_Extbase_Dispatcher::getSettings();
- $this->storagePageId = $extbaseSettings['storagePid'];
}
/**
*
* @param Tx_Extbase_Persistence_IdentityMap $identityMap
* @return void
- * @internal
*/
public function injectIdentityMap(Tx_Extbase_Persistence_IdentityMap $identityMap) {
$this->identityMap = $identityMap;
}
+ /**
+ * Injects the Reflection Service
+ *
+ * @param Tx_Extbase_Reflection_Service
+ * @return void
+ */
+ public function injectReflectionService(Tx_Extbase_Reflection_Service $reflectionService) {
+ $this->reflectionService = $reflectionService;
+ }
+
+ /**
+ * Injects the QueryFactory
+ *
+ * @param Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory
+ * @return void
+ */
+ public function injectQueryFactory(Tx_Extbase_Persistence_QueryFactoryInterface $queryFactory) {
+ $this->queryFactory = $queryFactory;
+ }
+
/**
* Injects the QueryObjectModelFactory
*
return $this->session;
}
+ /**
+ * Returns the Data Mapper
+ *
+ * @return Tx_Extbase_Persistence_Mapper_DataMapper
+ */
+ public function getDataMapper() {
+ return $this->dataMapper;
+ }
+
/**
* Returns the current QOM factory
*
* @return Tx_Extbase_Persistence_QOM_QueryObjectModelFactoryInterface
- * @internal
*/
public function getQOMFactory() {
return $this->QOMFactory;
* Returns the current value factory
*
* @return Tx_Extbase_Persistence_ValueFactoryInterface
- * @internal
*/
public function getValueFactory() {
return $this->valueFactory;
* Returns the current identityMap
*
* @return Tx_Extbase_Persistence_IdentityMap
- * @internal
*/
public function getIdentityMap() {
return $this->identityMap;
* @param object $object
* @return string The identifier for the object if it is known, or NULL
*/
- public function getUidByObject($object) {
+ public function getIdentifierByObject($object) {
if ($this->identityMap->hasObject($object)) {
- return $this->identityMap->getUidByObject($object);
+ return $this->identityMap->getIdentifierByObject($object);
} else {
return NULL;
}
}
+ /**
+ * Returns the object with the (internal) identifier, if it is known to the
+ * backend. Otherwise NULL is returned.
+ *
+ * @param string $identifier
+ * @param string $className
+ * @return object The object for the identifier if it is known, or NULL
+ */
+ public function getObjectByIdentifier($identifier, $className) {
+ if ($this->identityMap->hasIdentifier($identifier, $className)) {
+ return $this->identityMap->getObjectByIdentifier($identifier, $className);
+ } else {
+ $query = $this->queryFactory->create($className);
+ $result = $query->matching($query->withUid($identifier))->execute();
+ $object = NULL;
+ if (count($result) > 0) {
+ $object = current($result);
+ }
+ return $object;
+ }
+ }
+
/**
* Checks if the given object has ever been persisted.
*
* @return boolean TRUE if the object is new, FALSE if the object exists in the repository
*/
public function isNewObject($object) {
- return ($this->getUidByObject($object) === NULL);
+ return ($this->getIdentifierByObject($object) === NULL);
}
/**
* @return void
*/
public function replaceObject($existingObject, $newObject) {
- $existingUid = $this->getUidByObject($existingObject);
+ $existingUid = $this->getIdentifierByObject($existingObject);
if ($existingUid === NULL) throw new Tx_Extbase_Persistence_Exception_UnknownObject('The given object is unknown to this persistence backend.', 1238070163);
$this->identityMap->unregisterObject($existingObject);
* @return void
*/
protected function persistObjects() {
+ foreach ($this->aggregateRootObjects as $object) {
+ // if (!$this->identityMap->hasObject($object)) { // TODO Must be enabled to allow other identity properties than $uid
+ if ($object->_isNew()) {
+ $this->insertObject($object);
+ }
+ }
+
foreach ($this->aggregateRootObjects as $object) {
$this->persistObject($object);
}
}
/**
- * Inserts an objects corresponding row into the database. If the object is a value object an
- * existing instance will be looked up.
+ * Persists an object (instert, update) and its related objects (instert, update, delete).
*
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be inserted
* @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
* @param string $parentPropertyName The name of the property the object is stored in
* @return void
*/
- protected function persistObject($object, $parentObject = NULL, $parentPropertyName = NULL, $processQueue = TRUE) {
+ protected function persistObject(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
$row = array();
$queue = array();
$className = get_class($object);
$dataMap = $this->dataMapper->getDataMap($className);
+ $classSchema = $this->reflectionService->getClassSchema($className);
+
$properties = $object->_getProperties();
-
- if ($object instanceof Tx_Extbase_DomainObject_AbstractValueObject) {
- $this->checkForAlreadyPersistedValueObject($object);
- }
-
- // Fill up $row[$columnName] array with changed values which need to be stored
foreach ($properties as $propertyName => $propertyValue) {
- if (!$dataMap->isPersistableProperty($propertyName) || ($propertyValue instanceof Tx_Extbase_Persistence_LazyLoadingProxy)) {
+ if (!$dataMap->isPersistableProperty($propertyName)) continue;
+ if (($propertyValue instanceof Tx_Extbase_Persistence_LazyLoadingProxy) || ((get_class($propertyValue) === 'Tx_Extbase_Persistence_LazyObjectStorage') && ($propertyValue->isInitialized() === FALSE))) {
continue;
}
-
+
$columnMap = $dataMap->getColumnMap($propertyName);
- $columnName = $columnMap->getColumnName();
- if ($object->_isNew() || $object->_isDirty($propertyName)) {
- if ($columnMap->isRelation()) {
- if (($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) || ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY)) {
- $row[$columnName] = count($properties[$propertyName]);
- foreach ($propertyValue as $containedObject) {
- $queue[] = array($propertyName => $containedObject);
- }
- } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
- // TODO Handle Value Objects different
- // SK: this is the case RELATION_HAS_ONE, correct?
- if ($propertyValue->_isNew() || $propertyValue->_isDirty()) {
- // SK: What happens if the value is not new, but changed?
- $this->persistObject($propertyValue);
+ $propertyMetaData = $classSchema->getProperty($propertyName);
+ $propertyType = $propertyMetaData['type'];
+ // FIXME enable property-type check
+ // $this->checkPropertyType($propertyType, $propertyValue);
+ if (($propertyValue !== NULL) && $propertyType === 'Tx_Extbase_Persistence_ObjectStorage') {
+ if ($object->_isNew() || $object->_isDirty($propertyName)) {
+ $this->persistObjectStorage($propertyValue, $object, $propertyName, $queue, $row);
+ } else {
+ foreach ($propertyValue as $containedObject) {
+ $queue[] = $containedObject;
+ }
+ }
+ } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
+ if ($object->_isDirty($propertyName)) {
+ if ($propertyValue->_isNew()) {
+ if ($propertyValue instanceof Tx_Extbase_DomainObject_AbstractEntity) {
+ $this->insertObject($propertyValue, $object, $propertyName);
+ $queue[] = $propertyValue;
+ } else {
+ $this->persistValueObject($propertyValue, $object, $propertyName);
}
- $row[$columnName] = $propertyValue->getUid();
}
- } else {
- // Not an relation, this means it is a simple type such as STRING or Integer
- $row[$columnName] = $dataMap->convertPropertyValueToFieldValue($properties[$propertyName]);
+ $row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
}
+ } elseif ($object instanceof Tx_Extbase_DomainObject_AbstractValueObject || ($object->_isNew() || $object->_isDirty($propertyName))) {
+ $row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
}
- } // end property iteration for loop
-
- if ($object->_isNew()) {
- $this->insertObject($object, $parentObject, $parentPropertyName, $row);
- } elseif ($object->_isDirty()) {
- $this->updateObject($object, $parentObject, $parentPropertyName, $row);
}
- // SK: I need to check the code below more thoroughly
- if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && !empty($parentPropertyName)) {
- $parentClassName = get_class($parentObject);
- $parentDataMap = $this->dataMapper->getDataMap($parentClassName);
- $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
-
- if (($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY)) {
- $this->insertRelation($object, $parentObject, $parentPropertyName);
- }
+ if (count($row) > 0) {
+ $this->updateObject($object, $row);
}
-
+
if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity) {
$object->_memorizeCleanState();
}
+
+ foreach ($queue as $object) {
+ $this->persistObject($object);
+ }
- // SK: Where does $queue come from? Do we need the code below?
- if ($processQueue === TRUE) {
- foreach ($queue as $queuedObjects) {
- foreach($queuedObjects as $propertyName => $queuedObject) {
- $this->persistObject($queuedObject, $object, $propertyName);
- }
- }
+ }
+
+ /**
+ * Checks a value given against the expected type. If not matching, an
+ * UnexpectedTypeException is thrown. NULL is always considered valid.
+ *
+ * @param string $expectedType The expected type
+ * @param mixed $value The value to check
+ * @return void
+ * @throws Tx_Extbase_Persistence_Exception_UnexpectedType
+ */
+ protected function checkPropertyType($expectedType, $value) {
+ if ($value === NULL) {
+ return;
}
+ if (is_object($value)) {
+ if (!($value instanceof $expectedType)) {
+ throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Expected property of type ' . $expectedType . ', but got ' . get_class($value), 1244465558);
+ }
+ } elseif ($expectedType !== gettype($value)) {
+ throw new Tx_Extbase_Persistence_Exception_UnexpectedTypeException('Expected property of type ' . $expectedType . ', but got ' . gettype($value), 1244465558);
+ }
}
+
+ /**
+ * Persists the given value object.
+ *
+ * @return void
+ */
+ protected function persistValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName, $sortingPosition = 1) {
+ $result = $this->getUidOfAlreadyPersistedValueObject($object);
+ if ($result !== FALSE) {
+ $object->_setProperty('uid', (int)$result);
+ if($this->dataMapper->getDataMap(get_class($parentObject))->getColumnMap($parentPropertyName)->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+ $this->insertRelationInRelationtable($object, $parentObject, $parentPropertyName, $sortingPosition);
+ }
+ } elseif ($object->_isNew()) {
+ $row = array();
+ $className = get_class($object);
+ $dataMap = $this->dataMapper->getDataMap($className);
+ $classSchema = $this->reflectionService->getClassSchema($className);
+
+ $properties = $object->_getProperties();
+ foreach ($properties as $propertyName => $propertyValue) {
+ if (!$dataMap->isPersistableProperty($propertyName)) continue;
+ if (($propertyValue instanceof Tx_Extbase_Persistence_LazyLoadingProxy) || ((get_class($propertyValue) === 'Tx_Extbase_Persistence_LazyObjectStorage') && ($propertyValue->isInitialized() === FALSE))) {
+ continue;
+ }
- /*
- * Tests, if the given Domain Object already exists in the storage backend
+ $columnMap = $dataMap->getColumnMap($propertyName);
+ $propertyMetaData = $classSchema->getProperty($propertyName);
+ $propertyType = $propertyMetaData['type'];
+ // FIXME enable property-type check
+ // $this->checkPropertyType($propertyType, $propertyValue);
+ $row[$columnMap->getColumnName()] = $dataMap->convertPropertyValueToFieldValue($propertyValue);
+ }
+ $this->insertObject($object, $parentObject, $parentPropertyName, $sortingPosition, $row);
+ }
+ }
+
+ /**
+ * Tests, if the given Value Object already exists in the storage backend and if so, it returns the uid.
*
* @param Tx_Extbase_DomainObject_AbstractValueObject $object The object to be tested
*/
- protected function checkForAlreadyPersistedValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object) {
- $dataMap = $this->dataMapper->getDataMap(get_class($object));
- $properties = $object->_getProperties();
- $result = $this->storageBackend->hasValueObject($properties, $dataMap);
- if ($result !== FALSE) {
- $object->_setProperty('uid', $result);
+ protected function getUidOfAlreadyPersistedValueObject(Tx_Extbase_DomainObject_AbstractValueObject $object) {
+ return $this->storageBackend->getUidOfAlreadyPersistedValueObject($object);
+ }
+
+ /**
+ * Persists a relation. Objects of a 1:n or m:n relation are queued and processed with the parent object. A 1:1 relation
+ * gets persisted immediately. Objects which were removed from the property were detached from the parent object. They will not be
+ * deleted by default. You have to annotate the property with "@cascade remove" if you want them to be deleted as well.
+ *
+ * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object
+ * @param string $propertyName The name of the property the related objects are stored in
+ * @param mixed $propertyValue The property value
+ * @return void
+ */
+ protected function persistObjectStorage(Tx_Extbase_Persistence_ObjectStorage $objectStorage, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $propertyName, array &$queue, array &$row) {
+ $className = get_class($parentObject);
+ $columnMap = $this->dataMapper->getDataMap($className)->getColumnMap($propertyName);
+ $columnName = $columnMap->getColumnName();
+ $propertyMetaData = $this->reflectionService->getClassSchema($className)->getProperty($propertyName);
+
+ $updateParent = FALSE;
+ foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
+ if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') {
+ $this->removeObject($removedObject);
+ } else {
+ $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
+ }
+ $updateParent = TRUE;
+ }
+
+ $sortingPosition = 1;
+ foreach ($objectStorage as $object) {
+ if ($object->_isNew()) {
+ if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity) {
+ $this->insertObject($object, $parentObject, $propertyName, $sortingPosition);
+ $queue[] = $object;
+ } else {
+ $this->persistValueObject($object, $parentObject, $propertyName, $sortingPosition);
+ }
+ $updateParent = TRUE;
+ }
+ $sortingPosition++;
+ }
+
+ if ($updateParent === TRUE && $columnMap->getParentKeyFieldName() !== NULL) {
+ $row[$columnMap->getColumnName()] = $this->dataMapper->countRelated($parentObject, $propertyName);
}
}
-
+
+ /**
+ * Returns the removed objects determined by a comparison of the clean property value
+ * with the actual property value.
+ *
+ * @param Tx_Extbase_DomainObject_AbstractEntity $object The object
+ * @param string $parentPropertyName The name of the property
+ * @return array An array of removed objects
+ */
+ protected function getRemovedChildObjects(Tx_Extbase_DomainObject_AbstractEntity $object, $propertyName) {
+ $removedObjects = array();
+ $cleanPropertyValue = $object->_getCleanProperty($propertyName);
+ $propertyValue = $object->_getProperty($propertyName);
+ if ($cleanPropertyValue instanceof Tx_Extbase_Persistence_ObjectStorage) {
+ $cleanPropertyValue = $cleanPropertyValue->toArray();
+ }
+ if ($propertyValue instanceof Tx_Extbase_Persistence_ObjectStorage) {
+ $propertyValue = $propertyValue->toArray();
+ }
+ if ($cleanPropertyValue instanceof Iterator) {
+ foreach ($cleanPropertyValue as $hash => $item) {
+ if (!array_key_exists($hash, $propertyValue)) {
+ $removedObjects[] = $item;
+ }
+ }
+ }
+ return $removedObjects;
+ }
+
+ /**
+ * Updates the fields defining the relation between the object and the parent object.
+ *
+ * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
+ * @param Tx_Extbase_DomainObject_AbstractEntity $parentObject
+ * @param string $parentPropertyName
+ * @return void
+ */
+ protected function detachObjectFromParentObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject, $parentPropertyName) {
+ $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
+ $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
+ if ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
+ $row = array();
+ $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
+ if ($parentKeyFieldName !== NULL) {
+ $row[$parentKeyFieldName] = '';
+ $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
+ if ($parentTableFieldName !== NULL) {
+ $row[$parentTableFieldName] = '';
+ }
+ }
+ if (count($row) > 0) {
+ $this->updateObject($object, $row);
+ }
+ } elseif ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+ $this->deleteRelationFromRelationtable($object, $parentObject, $parentPropertyName);
+ }
+ }
+
/**
* Inserts an object in the storage
- *
+ *
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
- * @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
- * @param string|NULL $parentPropertyName The name of the property
- * @param array $row The $row
- */
- protected function insertObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
- $className = get_class($object);
- $dataMap = $this->dataMapper->getDataMap($className);
- $tableName = $dataMap->getTableName();
- $this->addCommonFieldsToRow($object, $parentObject, $parentPropertyName, $row);
- $uid = $this->storageBackend->addRow(
- $tableName,
- $row
- );
- $object->_setProperty('uid', $uid);
- $this->referenceIndex->updateRefIndexTable($tableName, $uid);
+ * @param array $row The tuple to be inserted
+ * @param Tx_Extbase_DomainObject_AbstractEntity $parentObject The parent object (if any)
+ * @param string $parentPropertyName The name of the property
+ */
+ protected function insertObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_AbstractEntity $parentObject = NULL, $propertyName = NULL, $sortingPosition = NULL, array $row = array()) {
+ $tableName = $this->dataMapper->getDataMap(get_class($object))->getTableName();
+ $this->addCommonFieldsToRow($object, $row);
+ if ($parentObject !== NULL) {
+ $parentColumnMap = $this->dataMapper->getDataMap(get_class($parentObject))->getColumnMap($propertyName);
+ if ($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY && $parentColumnMap->getParentKeyFieldName() !== NULL) {
+ $row[$parentColumnMap->getParentKeyFieldName()] = $parentObject->getUid();
+ }
+ }
+ if ($object->_isNew()) {
+ $uid = $this->storageBackend->addRow(
+ $tableName,
+ $row
+ );
+ $object->_setProperty('uid', (int)$uid);
+ }
+ if ($parentObject !== NULL) {
+ if($parentColumnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
+ $this->insertRelationInRelationtable($object, $parentObject, $propertyName, $sortingPosition);
+ }
+ }
+ if ($this->extbaseSettings['persistence']['updateReferenceIndex'] === '1') {
+ $this->referenceIndex->updateRefIndexTable($tableName, $uid);
+ }
+ $this->identityMap->registerObject($object, $uid);
}
-
+
/**
* Inserts mm-relation into a relation table
*
- * @param Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject The related object
+ * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The related object
* @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
- * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
+ * @param string $propertyName The name of the parent object's property where the related objects are stored in
* @return void
*/
- protected function insertRelation(Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
+ protected function insertRelationInRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $object, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $propertyName, $sortingPosition = NULL) {
$dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
+ $columnMap = $dataMap->getColumnMap($propertyName);
$row = array(
- 'uid_local' => (int)$parentObject->getUid(), // TODO Aliases for relation field names
- 'uid_foreign' => (int)$relatedObject->getUid(),
- 'tablenames' => $dataMap->getTableName(),
- 'sorting' => 9999 // TODO sorting of mm table items
+ $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
+ $columnMap->getChildKeyFieldName() => (int)$object->getUid(),
+ $columnMap->getChildSortByFieldName() => !is_null($sortingPosition) ? (int)$sortingPosition : 0
);
- $tableName = $dataMap->getColumnMap($parentPropertyName)->getRelationTableName();
+ $relationTableName = $columnMap->getRelationTableName();
+ // FIXME Reenable support for tablenames
+ // $childTableName = $columnMap->getChildTableName();
+ // if (isset($childTableName)) {
+ // $row['tablenames'] = $childTableName;
+ // }
$res = $this->storageBackend->addRow(
- $tableName,
+ $relationTableName,
$row,
TRUE);
return $res;
}
+ /**
+ * Delete an mm-relation from a relation table
+ *
+ * @param Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject The related object
+ * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
+ * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
+ * @return void
+ */
+ protected function deleteRelationFromRelationtable(Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
+ $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
+ $columnMap = $dataMap->getColumnMap($parentPropertyName);
+ $relationTableName = $columnMap->getRelationTableName();
+ $res = $this->storageBackend->removeRow(
+ $relationTableName,
+ array(
+ $columnMap->getParentKeyFieldName() => (int)$parentObject->getUid(),
+ $columnMap->getChildKeyFieldName() => (int)$relatedObject->getUid(),
+ ),
+ FALSE);
+ return $res;
+ }
+
/**
* Updates a given object in the storage
- *
+ *
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
* @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
* @param string|NULL $parentPropertyName The name of the property
* @param array $row The $row
*/
- protected function updateObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
- $className = get_class($object);
- $dataMap = $this->dataMapper->getDataMap($className);
- $tableName = $dataMap->getTableName();
- $this->addCommonFieldsToRow($object, $parentObject, $parentPropertyName, $row);
+ protected function updateObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, array &$row) {
+ $tableName = $this->dataMapper->getDataMap(get_class($object))->getTableName();
+ $this->addCommonFieldsToRow($object, $row);
$uid = $object->getUid();
$row['uid'] = $uid;
$res = $this->storageBackend->updateRow(
$tableName,
$row
);
- $this->referenceIndex->updateRefIndexTable($tableName, $uid);
+ if ($this->extbaseSettings['persistence']['updateReferenceIndex'] === '1') {
+ $this->referenceIndex->updateRefIndexTable($tableName, $uid);
+ }
+ if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity) {
+ $object->_memorizeCleanState();
+ }
return $res;
}
* @param array $properties The properties of the object
* @return array A single row to be inserted in the database
*/
- protected function addCommonFieldsToRow(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
+ protected function addCommonFieldsToRow(Tx_Extbase_DomainObject_DomainObjectInterface $object, array &$row) {
$className = get_class($object);
$dataMap = $this->dataMapper->getDataMap($className);
if ($dataMap->hasCreationDateColumn() && $object->_isNew()) {
if ($dataMap->hasTimestampColumn()) {
$row[$dataMap->getTimestampColumnName()] = $GLOBALS['EXEC_TIME'];
}
- if ($dataMap->hasPidColumn() && !isset($row['pid'])) {
- $row['pid'] = $this->storagePageId;
- }
- if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && !empty($parentPropertyName)) {
- $parentDataMap = $this->dataMapper->getDataMap(get_class($parentObject));
- $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
- $parentKeyFieldName = $parentColumnMap->getParentKeyFieldName();
- if ($parentKeyFieldName !== NULL) {
- $row[$parentKeyFieldName] = $parentObject->getUid();
- }
- $parentTableFieldName = $parentColumnMap->getParentTableFieldName();
- if ($parentTableFieldName !== NULL) {
- $row[$parentTableFieldName] = $parentDataMap->getTableName();
- }
+ if ($object->_isNew() && $dataMap->hasPidColumn() && !isset($row['pid'])) {
+ $row['pid'] = $this->determineStoragePageIdForNewRecord($object);
}
}
/**
- * Inserts and updates all relations of an object. It also inserts and updates data in relation tables.
- *
- * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object for which the relations should be updated
- * @param string $propertyName The name of the property holding the related child objects
- * @param array $relations The queued relations
- * @return void
- */
- protected function persistRelations(Tx_Extbase_DomainObject_DomainObjectInterface $object, $propertyName, array $relations) {
- $dataMap = $this->dataMapper->getDataMap(get_class($object));
- foreach ($relations as $propertyName => $relatedObjects) {
- if (!empty($relatedObjects)) {
- $typeOfRelation = $dataMap->getColumnMap($propertyName)->getTypeOfRelation();
- foreach ($relatedObjects as $relatedObject) {
- if ($relatedObject->_isNew()) {
- $this->persistObject($relatedObject, $object, $propertyName);
- if ($typeOfRelation === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
- $this->insertRelationInRelationTable($relatedObject, $object, $propertyName);
- }
- } elseif ($relatedObject->_isDirty()) {
- $this->persistObject($relatedObject, $object, $propertyName);
- }
- }
- }
- }
- }
-
- /**
- * Iterate over deleted objects and process them
+ * Iterate over deleted aggregate root objects and process them
*
* @return void
*/
protected function processDeletedObjects() {
foreach ($this->deletedObjects as $object) {
- $this->deleteObject($object);
- if ($this->identityMap->hasObject($object)) {
- $this->session->registerRemovedObject($object);
- $this->identityMap->unregisterObject($object);
- }
+ $this->removeObject($object);
+ $this->identityMap->unregisterObject($object);
}
$this->deletedObjects = new Tx_Extbase_Persistence_ObjectStorage();
}
/**
- * Deletes an object, it's 1:n related objects, and the m:n relations in relation tables (but not the m:n related objects!)
+ * Deletes an object
*
* @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to be insterted in the storage
* @param Tx_Extbase_DomainObject_AbstractEntity|NULL $parentObject The parent object (if any)
* @param string|NULL $parentPropertyName The name of the property
* @param bool $markAsDeleted Shold we only mark the row as deleted instead of deleting (TRUE by default)?
- * @param bool $recurseIntoRelations Shold we delete also dependant aggregates (FALSE by default)?
* @return void
*/
- protected function deleteObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, $markAsDeleted = TRUE, $recurseIntoRelations = FALSE) {
- // TODO Implement recursive deletions
+ protected function removeObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $markAsDeleted = TRUE) {
$dataMap = $this->dataMapper->getDataMap(get_class($object));
$tableName = $dataMap->getTableName();
- if ($markAsDeleted === TRUE && $dataMap->hasDeletedColumn()) {
+ if (($markAsDeleted === TRUE) && $dataMap->hasDeletedColumn()) {
$deletedColumnName = $dataMap->getDeletedColumnName();
$res = $this->storageBackend->updateRow(
$tableName,
} else {
$res = $this->storageBackend->removeRow(
$tableName,
- $object->getUid()
+ array('uid' => $object->getUid())
);
}
- $this->referenceIndex->updateRefIndexTable($tableName, $uid);
+ $this->removeRelatedObjects($object);
+ if ($this->extbaseSettings['persistence']['updateReferenceIndex'] === '1') {
+ $this->referenceIndex->updateRefIndexTable($tableName, $object->getUid());
+ }
}
-
+
/**
- * Deletes all relations of an object.
+ * Remove related objects
*
- * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object for which the relations should be updated
- * @param string $propertyName The name of the property holding the related child objects
- * @param array $relations The queued relations
+ * @param Tx_Extbase_DomainObject_DomainObjectInterface $object The object to scanned for related objects
* @return void
*/
- // SK: The below method is never called.
- // SK: I think there is still a problem with deleted objects and deleted relations.
- // SK: I am not yet sure where deleted relations ae handled. Need to check more thoroughly!
- protected function deleteRelatedObjects(Tx_Extbase_DomainObject_DomainObjectInterface $object, array $relations) {
- $dataMap = $this->dataMapper->getDataMap(get_class($object));
- foreach ($relations as $propertyName => $relatedObjects) {
- if (is_array($relatedObjects)) {
- foreach ($relatedObjects as $relatedObject) {
- $this->deleteObject($relatedObject, $object, $propertyName);
- if ($dataMap->getColumnMap($propertyName)->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
- // SK: This method does IMHO not exist.
- $this->deleteRelationInRelationTable($relatedObject, $object, $propertyName);
+ protected function removeRelatedObjects(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
+ $className = get_class($object);
+ $dataMap = $this->dataMapper->getDataMap($className);
+ $classSchema = $this->reflectionService->getClassSchema($className);
+
+ $properties = $object->_getProperties();
+ foreach ($properties as $propertyName => $propertyValue) {
+ $columnMap = $dataMap->getColumnMap($propertyName);
+ $propertyMetaData = $classSchema->getProperty($propertyName);
+ if ($propertyMetaData['cascade'] === 'remove') {
+ if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
+ foreach ($propertyValue as $containedObject) {
+ $this->removeObject($containedObject);
}
+ } elseif ($propertyValue instanceof Tx_Extbase_DomainObject_DomainObjectInterface) {
+ $this->removeObject($propertyValue);
}
}
}
}
- /**
- * Update relations in a relation table
- *
- * @param array $relatedObjects An array of related objects
- * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject The parent object
- * @param string $parentPropertyName The name of the parent object's property where the related objects are stored in
- * @return void
- */
- protected function deleteRelationInRelationTable($relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
- $dataMap = $this->dataMapper->getDataMap(get_class($parentObject));
- $tableName = $dataMap->getColumnMap($parentPropertyName)->getRelationTableName();
- // TODO Remove dependency to the t3lib_db instance
- $res = $this->persistenceBackend->exec_SELECTquery(
- 'uid_foreign',
- $tableName,
- 'uid_local=' . $parentObject->getUid()
- );
- $existingRelations = array();
- while($row = $this->persistenceBackend->sql_fetch_assoc($res)) {
- $existingRelations[current($row)] = current($row);
- }
- $relationsToDelete = $existingRelations;
- if (is_array($relatedObject)) {
- foreach ($relatedObject as $relatedObject) {
- $relatedObjectUid = $relatedObject->getUid();
- if (array_key_exists($relatedObjectUid, $relationsToDelete)) {
- unset($relationsToDelete[$relatedObjectUid]);
- }
- }
- }
- if (count($relationsToDelete) > 0) {
- $relationsToDeleteList = implode(',', $relationsToDelete);
- $res = $this->persistenceBackend->exec_DELETEquery(
- $tableName,
- 'uid_local=' . $parentObject->getUid() . ' AND uid_foreign IN (' . $relationsToDeleteList . ')'
- );
- }
- }
-
/**
* Delegates the call to the Data Map.
* Returns TRUE if the property is persistable (configured in $TCA)
$dataMap = $this->dataMapper->getDataMap($className);
return $dataMap->isPersistableProperty($propertyName);
}
+
+ /**
+ * Determine the storage page ID for a given NEW record
+ *
+ * This does the following:
+ * - If there is a TypoScript configuration "classes.CLASSNAME.newRecordStoragePid", that is used to store new records.
+ * - If there is no such TypoScript configuration, it uses the first value of The "storagePid" taken for reading records.
+ *
+ * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
+ * @return int the storage Page ID where the object should be stored
+ */
+ protected function determineStoragePageIdForNewRecord(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
+ $className = get_class($object);
+ $extbaseSettings = Tx_Extbase_Dispatcher::getExtbaseFrameworkConfiguration();
+
+ if (isset($extbaseSettings['persistence']['classes'][$className]) && !empty($extbaseSettings['persistence']['classes'][$className]['newRecordStoragePid'])) {
+ return (int)$extbaseSettings['persistence']['classes'][$className]['newRecordStoragePid'];
+ } else {
+ $storagePidList = t3lib_div::intExplode(',', $extbaseSettings['persistence']['storagePid']);
+ return (int) $storagePidList[0];
+ }
+ }
}