Extbase:
authorJochen Rau <j.rau@web.de>
Wed, 8 Apr 2009 22:43:56 +0000 (22:43 +0000)
committerJochen Rau <j.rau@web.de>
Wed, 8 Apr 2009 22:43:56 +0000 (22:43 +0000)
* Persistence layer completely revised
* Decoupled ORM from Persistence Session
* Order of CRUD now the same as in FLOW3
* Added addAll() and removeAll() to Object Storage
* Added Identity Map

typo3/sysext/extbase/Classes/Configuration/Manager.php
typo3/sysext/extbase/Classes/DomainObject/AbstractDomainObject.php
typo3/sysext/extbase/Classes/DomainObject/AbstractEntity.php
typo3/sysext/extbase/Classes/DomainObject/AbstractValueObject.php
typo3/sysext/extbase/Classes/MVC/View/Helper/URIHelper.php
typo3/sysext/extbase/Classes/Persistence/IdentityMap.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Persistence/Mapper/ObjectRelationalMapper.php
typo3/sysext/extbase/Classes/Persistence/ObjectStorage.php
typo3/sysext/extbase/Classes/Persistence/Repository.php
typo3/sysext/extbase/Classes/Persistence/RepositoryInterface.php
typo3/sysext/extbase/Classes/Persistence/Session.php

index c6c565c..807bebf 100644 (file)
@@ -73,8 +73,6 @@ class Tx_Extbase_Configuration_Manager implements t3lib_Singleton {
                                        $settings = $settings[$controllerName];
                                }
                        }
-                       // SK: TODO: Look at this in detail
-                       // JR: This is an overlay of TS settings; "local" values overwrite more "global" values
                        // TODO Should we provide a hierarchical TS setting overlay?
                        // if (!empty($controllerName) && is_array($settings[$controllerName])) {
                        //      foreach ($settings[$controllerName] as $key => $value) {
index 2d3e5e7..b3123f8 100644 (file)
@@ -37,11 +37,6 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas
        protected $uid;
 
        /**
-        * @var An array holding the clean property values. Set right after reconstitution of the object
-        */
-       private $_cleanProperties = NULL;
-
-       /**
         * The generic constructor. If you want to implement your own __constructor() method in your Domain Object you have to call
         * $this->initializeObject() in the first line of your constructor.
         *
@@ -59,8 +54,8 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas
         * @return void
         */
        public function __wakeup() {
-               foreach ($GLOBALS['Extbase']['reconstituteObject']['properties'] as $propertyName => $value) {
-                       $this->_reconstituteProperty($propertyName, $value);
+               foreach ($GLOBALS['Extbase']['reconstituteObject']['properties'] as $propertyName => $propertyValue) {
+                       $this->_reconstituteProperty($propertyName, $propertyValue);
                }
                $this->initializeObject();
        }
@@ -96,23 +91,7 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas
                        $this->$propertyName = $value;
                }
        }
-
-       /**
-        * Register an object's clean state, e.g. after it has been reconstituted
-        * from the database
-        *
-        * @return void
-        * @internal
-        */
-       public function _memorizeCleanState() {
-               $this->initializeCleanProperties();
-               $cleanProperties = array();
-               foreach ($this->_cleanProperties as $propertyName => $propertyValue) {
-                       $cleanProperties[$propertyName] = $this->$propertyName;
-               }
-               $this->_cleanProperties = $cleanProperties;
-       }
-
+       
        /**
         * Returns a hash map of property names and property values
         *
@@ -124,42 +103,48 @@ abstract class Tx_Extbase_DomainObject_AbstractDomainObject implements Tx_Extbas
                unset($properties['_cleanProperties']);
                return $properties;
        }
-
+       
        /**
-        * Returns a hash map of dirty properties and $values
+        * Returns TRUE if the object is new (the uid was not set, yet)
         *
         * @return boolean
         * @internal
         */
+       // TODO Discuss, if this is the right way to say _isNew()
+       public function _isNew() {
+               return $this->uid === NULL;
+       }
+       
+       /**
+        * Register an object's clean state, e.g. after it has been reconstituted
+        * from the database
+        *
+        * @return void
+        * @internal
+        */
+       public function _memorizeCleanState() {
+       }
+
+       /**
+        * Returns a hash map of dirty properties and $values. This is always the empty array for ValueObjects, because ValueObjects never change.
+        *
+        * @return array
+        * @internal
+        */
        public function _getDirtyProperties() {
-               if (!is_array($this->_cleanProperties)) throw new Tx_Extbase_Persistence_Exception_CleanStateNotMemorized('The clean state of the object "' . get_class($this) . '" has not been memorized before asking _isDirty().', 1233309106);
-               if ($this->uid !== NULL && $this->uid != $this->_cleanProperties['uid']) throw new Tx_Extbase_Persistence_Exception_TooDirty('The uid "' . $this->uid . '" has been modified, that is simply too much.', 1222871239);
-               $dirtyProperties = array();
-               foreach ($this->_cleanProperties as $propertyName => $propertyValue) {
-                       if ($this->$propertyName !== $propertyValue) {
-                               $dirtyProperties[$propertyName] = $this->$propertyName;
-                       }
-               }
-               return $dirtyProperties;
+               return array();
        }
 
        /**
-        * Saves a copy of values of the persitable properties inside the object itself. This method is normally
-        * called right after it's reconstitution from a storage.
+        * Returns TRUE if the properties were modified after reconstitution. However, value objects can be never updated.
         *
-        * @return void
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
+        * @return boolean
+        * @internal
         */
-       private function initializeCleanProperties() {
-               $properties = get_object_vars($this);
-               $dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper'); // singleton
-               foreach ($properties as $propertyName => $propertyValue) {
-                       if ($dataMapper->isPersistableProperty(get_class($this), $propertyName)) {
-                               $this->_cleanProperties[$propertyName] = NULL;
-                       }
-               }
-               $this->_cleanProperties['uid'] = NULL;
+       public function _isDirty() {
+               return FALSE;
        }
+       
 
 }
 ?>
\ No newline at end of file
index 343cbb4..66f7ea3 100644 (file)
@@ -31,6 +31,7 @@
  * @version $ID:$
  */
 abstract class Tx_Extbase_DomainObject_AbstractEntity extends Tx_Extbase_DomainObject_AbstractDomainObject {
+
        /**
         * @var An array holding the clean property values. Set right after reconstitution of the object
         */
@@ -44,30 +45,14 @@ abstract class Tx_Extbase_DomainObject_AbstractEntity extends Tx_Extbase_DomainO
         * @internal
         */
        public function _memorizeCleanState() {
-               $this->initializeCleanProperties();
-               $cleanProperties = array();
-               foreach ($this->_cleanProperties as $propertyName => $propertyValue) {
-                       $cleanProperties[$propertyName] = $this->$propertyName;
-               }
-               $this->_cleanProperties = $cleanProperties;
-       }
-
-       /**
-        * Saves a copy of values of the persitable properties inside the object itself. This method is normally
-        * called right after it's reconstitution from a storage.
-        *
-        * @return void
-        * @author Jochen Rau <jochen.rau@typoplanet.de>
-        */
-       private function initializeCleanProperties() {
-               $properties = get_object_vars($this);
                $dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper'); // singleton
+               $this->_cleanProperties = array();
+               $properties = get_object_vars($this);
                foreach ($properties as $propertyName => $propertyValue) {
                        if ($dataMapper->isPersistableProperty(get_class($this), $propertyName)) {
-                               $this->_cleanProperties[$propertyName] = NULL;
+                               $this->_cleanProperties[$propertyName] = $this->$propertyName;
                        }
                }
-               $this->_cleanProperties['uid'] = NULL;
        }
 
        /**
@@ -94,13 +79,22 @@ abstract class Tx_Extbase_DomainObject_AbstractEntity extends Tx_Extbase_DomainO
         * @return boolean
         * @internal
         */
-       public function _isDirty() {
+       public function _isDirty($propertyName = NULL) {
                if (!is_array($this->_cleanProperties)) throw new Tx_Extbase_Persistence_Exception_CleanStateNotMemorized('The clean state of the object "' . get_class($this) . '" has not been memorized before asking _isDirty().', 1233309106);
                if ($this->uid !== NULL && $this->uid != $this->_cleanProperties['uid']) throw new Tx_Extbase_Persistence_Exception_TooDirty('The uid "' . $this->uid . '" has been modified, that is simply too much.', 1222871239);
-               foreach ($this->_cleanProperties as $propertyName => $propertyValue) {
-                       if ($this->$propertyName !== $propertyValue) return TRUE;
+               $result = FALSE;
+               if ($propertyName !== NULL) {
+                       $result = $this->_cleanProperties[$propertyName] !== $this->$propertyName;
+               } else {
+                       foreach ($this->_cleanProperties as $propertyName => $propertyValue) {
+                               if ($this->$propertyName !== $propertyValue) {
+                                       $result = TRUE;
+                                       break;
+                               }
+                       }
                }
-               return FALSE;
+               return $result;
        }
+                       
 }
 ?>
\ No newline at end of file
index 126381a..5c78173 100644 (file)
 abstract class Tx_Extbase_DomainObject_AbstractValueObject extends Tx_Extbase_DomainObject_AbstractDomainObject {
 
        /**
-        * Register an object's clean state, e.g. after it has been reconstituted
-        * from the database
+        * Returns the value of the Value Object. Must be overwritten by a concrete value object.
         *
         * @return void
-        * @internal
         */
-       public function _memorizeCleanState() {
-       }
-
-       /**
-        * Returns a hash map of dirty properties and $values. This is always the empty array for ValueObjects, because ValueObjects never change.
-        *
-        * @return array
-        * @internal
-        */
-       public function _getDirtyProperties() {
-               return array();
-       }
-
-       /**
-        * Returns TRUE if the properties were modified after reconstitution. However, value objects can be never updated.
-        *
-        * @return boolean
-        * @internal
-        */
-       public function _isDirty() {
-               return FALSE;
+       public function getValue() {
+               return $this->__toString();
        }
+       
 }
 ?>
\ No newline at end of file
index a8e7aa8..232f557 100644 (file)
@@ -22,6 +22,7 @@ require_once(PATH_tslib . 'class.tslib_content.php');
  * @version $Id:$
  */
 class Tx_Extbase_MVC_View_Helper_URIHelper implements t3lib_Singleton {
+
        /**
         * An instance of tslib_cObj
         *
diff --git a/typo3/sysext/extbase/Classes/Persistence/IdentityMap.php b/typo3/sysext/extbase/Classes/Persistence/IdentityMap.php
new file mode 100644 (file)
index 0000000..124e2b1
--- /dev/null
@@ -0,0 +1,121 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+
+/**
+ * An Idetity Map for Domain Objects
+ *
+ * @package TYPO3
+ * @subpackage extbase
+ * @version $ID:$
+ */
+class Tx_Extbase_Persistence_IdentityMap {
+
+       /**
+        * @var Tx_Extbase_Persistence_ObjectStorage
+        */
+       protected $objectMap;
+
+       /**
+        * @var array
+        */
+       protected $uidMap = array();
+
+       /**
+        * Constructs a new Identity Map
+        */
+       public function __construct() {
+               $this->objectMap = new Tx_Extbase_Persistence_ObjectStorage();
+       }
+
+       /**
+        * Checks whether the given object is known to the identity map
+        *
+        * @param object $object
+        * @return boolean
+        */
+       public function hasObject($object) {
+               return $this->objectMap->contains($object);
+       }
+
+       /**
+        * Checks whether the given uiduid is known to the identity map
+        *
+        * @param string $className
+        * @param string $uid
+        * @return boolean
+        */
+       public function hasUid($className, $uid) {
+               if (is_array($this->uidMap[$className])) {
+                       return array_key_exists($uid, $this->uidMap[$className]);
+               } else {
+                       return FALSE;
+               }
+       }
+
+       /**
+        * Returns the object for the given uid
+        *
+        * @param string $className
+        * @param string $uid
+        * @return object
+        */
+       public function getObjectByUid($className, $uid) {
+               return $this->uidMap[$className][$uid];
+       }
+
+       /**
+        * Returns the node identifier for the given object
+        *
+        * @param object $object
+        * @return string
+        */
+       public function getUidByObject($object) {
+               return $this->objectMap[$object];
+       }
+
+       /**
+        * Register a node identifier for an object
+        *
+        * @param object $object
+        * @param string $uid
+        */
+       public function registerObject($object, $uid) {
+               $this->objectMap[$object] = $uid;
+               $this->uidMap[get_class($object)][$uid] = $object;
+       }
+
+       /**
+        * Unregister an object
+        *
+        * @param string $object
+        * @return void
+        */
+       public function unregisterObject($object) {
+               unset($this->uidMap[get_class($object)][$this->objectMap[$object]]);
+               $this->objectMap->detach($object);
+       }
+
+}
+
+?>
\ No newline at end of file
index 4c1d2ce..aa73aa3 100644 (file)
@@ -35,13 +35,6 @@ require_once(PATH_tslib . 'class.tslib_content.php');
 class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Singleton {
 
        /**
-        * The persistence session
-        *
-        * @var Tx_Extbase_Persistence_Session
-        **/
-       protected $persistenceSession;
-
-       /**
         * Cached data maps
         *
         * @var array
@@ -63,12 +56,19 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
        protected $referenceIndex;
        
        /**
-        * Statistics with counts of database operations
+        * The aggregate root objects to be handled by the object relational mapper
         *
-        * @var array
+        * @var Tx_Extbase_Persistence_ObjectStorage
         **/
-       protected $statistics = array();
-       
+       protected $aggregateRootObjects;
+
+       /**
+        * The deleted objects to be handled by the object relational mapper
+        *
+        * @var Tx_Extbase_Persistence_ObjectStorage
+        **/
+       protected $deletedObjects;
+
        /**
         * A first level cache for domain objects by class and uid
         *
@@ -81,11 +81,32 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         *
         */
        public function __construct() {
-               $this->persistenceSession = t3lib_div::makeInstance('Tx_Extbase_Persistence_Session');
+               $this->aggregateRootObjects = new Tx_Extbase_Persistence_ObjectStorage();
+               $this->identityMap = new Tx_Extbase_Persistence_IdentityMap();
                $GLOBALS['TSFE']->includeTCA();
                $this->database = $GLOBALS['TYPO3_DB'];
                $this->referenceIndex = t3lib_div::makeInstance('t3lib_refindex');
        }
+       
+       /**
+        * Sets the aggregate root objects
+        *
+        * @param Tx_Extbase_Persistence_ObjectStorage $objects The objects to be registered
+        * @return void
+        */
+       public function setAggregateRootObjects(Tx_Extbase_Persistence_ObjectStorage $objects) {
+               $this->aggregateRootObjects = $objects;
+       }
+
+       /**
+        * Sets the deleted objects
+        *
+        * @param Tx_Extbase_Persistence_ObjectStorage $objects The objects to be deleted
+        * @return void
+        */
+       public function setDeletedObjects(Tx_Extbase_Persistence_ObjectStorage $objects) {
+               $this->deletedObjects = $objects;
+       }
 
        /**
         * The build query method is invoked by the Persistence Repository.
@@ -127,7 +148,7 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         *
         * @return string The where part
         */
-       protected function buildQueryByConditions(Tx_Extbase_Persistence_Mapper_DataMap &$dataMap, $conditions) {
+       protected function buildQueryByConditions(Tx_Extbase_Persistence_Mapper_DataMap &$dataMap, array $conditions) {
                $whereParts = array();
                foreach ($conditions as $key => $condition) {
                        if (is_array($condition) && isset($condition[0])) {
@@ -227,7 +248,6 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                        $orderBy,
                        $limit
                        );
-               $this->statistics['select']++;
 
                $fieldMap = $this->getFieldMapFromResult($res);
                $rows = $this->getRowsFromResult($res);
@@ -243,7 +263,6 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                                $objects = $this->reconstituteObjects($dataMap, $fieldMap, $rows);
                        }
                }
-               $this->statistics['fetch']++;
                return $objects;
        }
        
@@ -269,7 +288,7 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                $fieldMap = array();
                if ($res !== FALSE) {
                        $fieldPosition = 0;
-                       // FIXME mysql_fetch_field should be available in t3lib_db (patch core)
+                       // TODO mysql_fetch_field should be available in t3lib_db (patch core)
                        while ($field = mysql_fetch_field($res)) {
                                $fieldMap[$field->table][$field->name] = $fieldPosition;
                                $fieldPosition++;
@@ -288,13 +307,13 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
        }
        
        /**
-        * Performs woekspace and language overlay on the given row array..The language and workspaceid is automatically 
-        * detected (depending on FE or BE context). You can also explicitly set the language/workspace uid.
+        * Performs workspace and language overlay on the given row array. The language and workspace id is automatically 
+        * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
         *
         * @param Tx_Extbase_Persistence_Mapper_DataMap $dataMap 
         * @param array $row The row array (as reference) 
-        * @param string $languageUid The language uid
-        * @param string $workspaceUidUid The workspace uid
+        * @param string $languageUid The language id
+        * @param string $workspaceUidUid The workspace id
         * @return void
         */
        protected function doLanguageAndWorkspaceOverlay(Tx_Extbase_Persistence_Mapper_DataMap $dataMap, array &$fieldMap, array &$row, $languageUid = null, $workspaceUid = null) {
@@ -309,13 +328,10 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                        else {
                                require_once(PATH_t3lib . 'class.t3lib_page.php');
                                $pageSelectObject = t3lib_div::makeInstance('t3lib_pageSelect');
-
                                if ($languageUid === NULL) {
                                        $languageUid = intval(t3lib_div::_GP('L'));
                                }
                        }
-
-
                        if ($workspaceUid !== NULL) {
                                $pageSelectObject->versioningWorkspaceId = $workspaceUid;
                        }
@@ -370,12 +386,11 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                $objects = array();
                foreach ($rows as $row) {
                        $properties = $this->getProperties($dataMap, $fieldMap, $row);
-                       $identity = $properties['uid'];
                        $className = $dataMap->getClassName();
-                       if (isset($this->identityMap[$className][$identity])) {
-                               $object = $this->identityMap[$className][$identity];
+                       if ($this->identityMap->hasUid($className, $properties['uid'])) {
+                               $object = $this->identityMap->getObjectByUid($className, $properties['uid']);
                        } else {
-                               $object = $this->reconstituteObject($dataMap->getClassName(), $properties);
+                               $object = $this->reconstituteObject($dataMap->getClassName(), $properties);                             
                                foreach ($dataMap->getColumnMaps() as $columnMap) {
                                        if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) {
                                                list($relatedObject) = $this->reconstituteObjects($this->getDataMap($columnMap->getChildClassName()), $fieldMap, array($row));
@@ -388,10 +403,11 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                                                $relatedObjects = $this->fetchWithRelationTable($object, $columnMap);
                                                $object->_reconstituteProperty($columnMap->getPropertyName(), $relatedObjects);
                                        }
-                               }
-                               $this->persistenceSession->registerReconstitutedObject($object);
-                               $this->identityMap[$className][$identity] = $object;
+                               }                               
+                               $object->_memorizeCleanState();
+                               $this->identityMap->registerObject($object, $properties['uid']);
                        }
+                       
                        $objects[] = $object;
                }
                return $objects;
@@ -426,191 +442,145 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                $GLOBALS['Extbase']['reconstituteObject']['properties'] = $properties;
                $object = unserialize('O:' . strlen($className) . ':"' . $className . '":0:{};');
                unset($GLOBALS['Extbase']['reconstituteObject']);
-               $this->statistics['reconstitute']++;
                return $object;
        }
 
        /**
-        * Persists all objects of a persistence session
+        * Create a database entry for all aggregate roots first, then traverse object graph.
         *
         * @return void
         */
-       public function persistAll() {
-               // first, persist all aggregate root objects
-               $aggregateRootClassNames = $this->persistenceSession->getAggregateRootClassNames();
-               foreach ($aggregateRootClassNames as $className) {
-                       $this->persistObjects($className);
-               }
-               // persist all remaining objects registered manually
-               $this->persistObjects();
-               // TODO delete objects that are not an aggregate root and lost connection to the parent
-               // TODO delete value objects that have zero reference count (get from reference index)
-       }
-
-       /**
-        * Persists all objects of a persitance persistence session that are of a given class. If there
-        * is no class specified, it persits all objects of a persistence session.
-        *
-        * @param string $className Name of the class of the objects to be persisted
-        */
-       protected function persistObjects($className = NULL) {
-               foreach ($this->persistenceSession->getAddedObjects($className) as $object) {
-                       $this->insertObject($object);
-                       $this->persistenceSession->unregisterObject($object);
-                       $this->persistenceSession->registerReconstitutedObject($object);
-               }
-               foreach ($this->persistenceSession->getRemovedObjects($className) as $object) {
-                       $this->deleteObject($object);
-                       $this->persistenceSession->unregisterObject($object);
-               }
-               foreach ($this->persistenceSession->getDirtyObjects($className) as $object) {
-                       $this->updateObject($object);
-                       $this->persistenceSession->unregisterObject($object);
-                       $this->persistenceSession->registerReconstitutedObject($object);
+       public function persistObjects() {
+               foreach ($this->aggregateRootObjects as $object) {
+                       $this->persistObject($object);
                }
        }
-
+       
        /**
-        * Inserts an object to the database. If the object is a value object an
+        * Inserts an object's corresdponding row into the database. If the object is a value object an
         * existing instance will be looked up.
         *
-        * @param Tx_Extbase_DomainObject_DomainObjectInterface $object
-        * @param Tx_Extbase_DomainObject_DomainObjectInterface $parentObject
-        * @param string $parentPropertyName
-        * @param string $recurseIntoRelations
+        * @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 insertObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, $recurseIntoRelations = TRUE) {
-               $properties = $object->_getProperties();
+       protected function persistObject($object, $parentObject = NULL, $parentPropertyName = NULL, $processQueue = TRUE) {
+               $queue = array();
                $className = get_class($object);
-               $dataMap = $this->getDataMap($className);
-               $row = $this->getRowToBeInstertedOrUpdated($dataMap, $properties);
-               
+               $dataMap = $this->getDataMap($className);                               
+               $properties = $object->_getProperties();
+                               
                if ($object instanceof Tx_Extbase_DomainObject_AbstractValueObject) {
                        $conditions = $properties;
                        unset($conditions['uid']);
                        $where = $this->buildQuery($className, $conditions);
                        $existingValueObjects = $this->fetch($className, $where);
-                       if (count($existingValueObjects)) {
+                       if (count($existingValueObjects) > 0) {
                                $existingObject = $existingValueObjects[0];
                                $object->_reconstituteProperty('uid', $existingObject->getUid());
-                               return;
                        }
                }
 
-               if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && $parentPropertyName !== NULL) {
-                       $parentDataMap = $this->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();
+               foreach ($properties as $propertyName => $propertyValue) {
+                       $columnMap = $dataMap->getColumnMap($propertyName);
+                       $columnName = $columnMap->getColumnName();
+                       if ($dataMap->isPersistableProperty($propertyName) && ($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 ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_ONE) {
+                                               $queue[] = array($propertyName => $propertyValue);
+                                       }
+                               } else {
+                                       $row[$columnName] = $dataMap->convertPropertyValueToFieldValue($properties[$propertyName], FALSE);
+                               }
                        }
                }
+               
+               if ($object->_isNew()) {
+                       $this->insertObject($object, $parentObject, $parentPropertyName, &$row);
+               } elseif ($object->_isDirty()) {
+                       $this->updateObject($object, $parentObject, $parentPropertyName, &$row);
+               }
+               
+               if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && !empty($parentPropertyName)) {
+                       $parentClassName = get_class($parentObject);
+                       $parentDataMap = $this->getDataMap($parentClassName);
+                       $parentColumnMap = $parentDataMap->getColumnMap($parentPropertyName);
 
-               if ($dataMap->hasPidColumn()) {
-                       $row['pid'] = !empty($this->cObj->data['pages']) ? $this->cObj->data['pages'] : $GLOBALS['TSFE']->id;
+                       if (($parentColumnMap->getTypeOfRelation()  === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY)) {
+                               $this->insertRelation($object, $parentObject, $parentPropertyName);     
+                       }
                }
-               if ($dataMap->hasCreationDateColumn()) {
-                       $row[$dataMap->getCreationDateColumnName()] = time();
+                               
+               if ($object instanceof Tx_Extbase_DomainObject_AbstractEntity) {
+                       $object->_memorizeCleanState();
                }
-               if ($dataMap->hasTimestampColumn()) {
-                       $row[$dataMap->getTimestampColumnName()] = time();
+               if ($processQueue === TRUE) {
+                       foreach ($queue as $queuedObjects) {
+                               foreach($queuedObjects as $propertyName => $queuedObject) {
+                                       $this->persistObject($queuedObject, $object, $propertyName);
+                               }
+                       }
                }
-               unset($row['uid']);
+       
+       }
+       
+       protected function insertObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
+               $className = get_class($object);
+               $dataMap = $this->getDataMap($className);                               
                $tableName = $dataMap->getTableName();
+               $this->addCommonFieldsToRow($object, $parentObject, $parentPropertyName, &$row);
                $res = $this->database->exec_INSERTquery(
                        $tableName,
                        $row
                        );
-               $this->statistics['insert']++;
                $uid = $this->database->sql_insert_id();
                $object->_reconstituteProperty('uid', $uid);
-               
                $this->referenceIndex->updateRefIndexTable($tableName, $uid);
-
-               $this->persistRelations($object, $propertyName, $this->getRelations($dataMap, $properties));
        }
-
+       
        /**
-        * Updates a modified object in the database
+        * Inserts relation into 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 updateObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, $recurseIntoRelations = TRUE) {
-               $properties = $object->_getDirtyProperties();
-               $dataMap = $this->getDataMap(get_class($object));
-               $row = $this->getRowToBeInstertedOrUpdated($dataMap, $properties);
-
-               if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && $parentPropertyName !== NULL) {
-                       $parentDataMap = $this->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();
-                       }
-               }
-
-               unset($row['uid']);
-               if ($dataMap->hasTimestampColumn()) {
-                       $row[$dataMap->getTimestampColumnName()] = time();
-               }
-               if ($dataMap->hasCreatorUidColumn() && !empty($GLOBALS['TSFE']->fe_user->user['uid'])) {
-                       $row[$dataMap->getCreatorUidColumnName()] = $GLOBALS['TSFE']->fe_user->user['uid'];
-               }
+       protected function insertRelation(Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
+               $dataMap = $this->getDataMap(get_class($parentObject));
+               $rowToInsert = array(
+                       'uid_local' => $parentObject->getUid(),
+                       'uid_foreign' => $relatedObject->getUid(),
+                       'tablenames' => $dataMap->getTableName(),
+                       'sorting' => 9999 // TODO sorting of mm table items
+                       );
+               $tableName = $dataMap->getColumnMap($parentPropertyName)->getRelationTableName();
+               $res = $this->database->exec_INSERTquery(
+                       $tableName,
+                       $rowToInsert
+                       );
+       }
+       
+       protected function updateObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
+               $className = get_class($object);
+               $dataMap = $this->getDataMap($className);                               
                $tableName = $dataMap->getTableName();
+               $this->addCommonFieldsToRow($object, $parentObject, $parentPropertyName, &$row);
+               $uid = $object->getUid();
                $res = $this->database->exec_UPDATEquery(
                        $tableName,
-                       'uid=' . $object->getUid(),
+                       'uid=' . intval($uid),
                        $row
                        );
-               $this->statistics['update']++;
-
-               $this->persistRelations($object, $propertyName, $this->getRelations($dataMap, $properties));
-       }
-
-       /**
-        * Deletes an object, it's 1:n related objects, and the m:n relations in relation tables (but not the m:n related objects!)
-        *
-        * @return void
-        */
-       protected function deleteObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, $recurseIntoRelations = FALSE, $onlySetDeleted = TRUE) {
-               $relations = array();
-               $properties = $object->_getDirtyProperties();
-               $dataMap = $this->getDataMap(get_class($object));
-               $relations = $this->getRelations($dataMap, $properties);
-
-               $tableName = $dataMap->getTableName();
-               if ($onlySetDeleted === TRUE && $dataMap->hasDeletedColumn()) {
-                       $deletedColumnName = $dataMap->getDeletedColumnName();
-                       $res = $this->database->exec_UPDATEquery(
-                               $tableName,
-                               'uid=' . $object->getUid(),
-                               array($deletedColumnName => 1)
-                               );
-                       $this->statistics['update']++;
-               } else {
-                       $res = $this->database->exec_DELETEquery(
-                               $tableName,
-                               'uid=' . $object->getUid()
-                               );
-                       $this->statistics['delete']++;
-               }
                $this->referenceIndex->updateRefIndexTable($tableName, $uid);
-
-               if ($recurseIntoRelations === TRUE) {
-                       // FIXME disabled, recursive delete has to be implemented
-                       // $this->processRelations($object, $propertyName, $relations);
-               }
        }
-
+       
        /**
         * Returns a table row to be inserted or updated in the database
         *
@@ -618,23 +588,31 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         * @param array $properties The properties of the object
         * @return array A single row to be inserted in the database
         */
-       protected function getRowToBeInstertedOrUpdated(Tx_Extbase_Persistence_Mapper_DataMap $dataMap, $properties) {
-               $relations = array();
-               foreach ($dataMap->getColumnMaps() as $columnMap) {
-                       $propertyName = $columnMap->getPropertyName();
-                       $columnName = $columnMap->getColumnName();
-                       if ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_MANY) {
-                               $row[$columnName] = count($properties[$propertyName]);
-                       } elseif ($columnMap->getTypeOfRelation() === Tx_Extbase_Persistence_Mapper_ColumnMap::RELATION_HAS_AND_BELONGS_TO_MANY) {
-                               // TODO Check if this elseif is needed or could be merged with the lines above
-                               $row[$columnName] = count($properties[$propertyName]);
-                       } else {
-                               if ($properties[$propertyName] !== NULL) {
-                                       $row[$columnName] = $dataMap->convertPropertyValueToFieldValue($properties[$propertyName], FALSE);
-                               }
+       protected function addCommonFieldsToRow(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, array &$row) {
+               $className = get_class($object);
+               $dataMap = $this->getDataMap($className);
+               if ($dataMap->hasCreationDateColumn()) {
+                       $row[$dataMap->getCreationDateColumnName()] = time();
+               }
+               if ($dataMap->hasTimestampColumn()) {
+                       $row[$dataMap->getTimestampColumnName()] = time();
+               }               
+               if ($dataMap->hasPidColumn()) {
+                       // FIXME check, if this really works: settings from $this->cObj must be merged into the extension settings in the dispatcher
+                       $row['pid'] = !empty($this->cObj->data['pages']) ? $this->cObj->data['pages'] : $GLOBALS['TSFE']->id;
+               }
+               if ($parentObject instanceof Tx_Extbase_DomainObject_DomainObjectInterface && !empty($parentPropertyName)) {
+                       $parentDataMap = $this->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();
                        }
                }
-               return $row;
        }
 
        /**
@@ -644,11 +622,13 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         * @param string $properties The object properties
         * @return array An array of properties with related child objects
         */
-       protected function getRelations(Tx_Extbase_Persistence_Mapper_DataMap $dataMap, $properties) {
+       protected function getRelations(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
+               $className = get_class($object);
+               $dataMap = $this->getDataMap($className);
+               $properties = $object->_getProperties();
                $relations = array();
                foreach ($dataMap->getColumnMaps() as $columnMap) {
                        $propertyName = $columnMap->getPropertyName();
-                       $columnName = $columnMap->getColumnName();
                        if ($columnMap->isRelation()) {
                                $relations[$propertyName] = $properties[$propertyName];
                        }
@@ -670,18 +650,50 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                        if (!empty($relatedObjects)) {
                                $typeOfRelation = $dataMap->getColumnMap($propertyName)->getTypeOfRelation();
                                foreach ($relatedObjects as $relatedObject) {
-                                       if (!$this->persistenceSession->isReconstitutedObject($relatedObject)) {
-                                               $this->insertObject($relatedObject, $object, $propertyName);
+                                       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 ($this->persistenceSession->isReconstitutedObject($relatedObject) && $relatedObject->_isDirty()) {
-                                               $this->updateObject($relatedObject, $object, $propertyName);
+                                       } elseif ($relatedObject->_isDirty()) {
+                                               $this->persistObject($relatedObject, $object, $propertyName);
                                        }
                                }
                        }
                }
        }
+       
+       public function processDeletedObjects() {
+               foreach ($this->deletedObjects as $object) {
+                       $this->deleteObject($object);
+               }               
+       }
+
+       /**
+        * Deletes an object, it's 1:n related objects, and the m:n relations in relation tables (but not the m:n related objects!)
+        *
+        * @return void
+        */
+       protected function deleteObject(Tx_Extbase_DomainObject_DomainObjectInterface $object, $parentObject = NULL, $parentPropertyName = NULL, $recurseIntoRelations = TRUE, $onlySetDeleted = TRUE) {
+               $properties = $object->_getProperties();
+               $dataMap = $this->getDataMap(get_class($object));
+
+               $tableName = $dataMap->getTableName();
+               if ($onlySetDeleted === TRUE && $dataMap->hasDeletedColumn()) {
+                       $deletedColumnName = $dataMap->getDeletedColumnName();
+                       $res = $this->database->exec_UPDATEquery(
+                               $tableName,
+                               'uid=' . intval($object->getUid()),
+                               array($deletedColumnName => 1)
+                               );
+               } else {
+                       $res = $this->database->exec_DELETEquery(
+                               $tableName,
+                               'uid=' . intval($object->getUid())
+                               );
+               }
+               $this->referenceIndex->updateRefIndexTable($tableName, $uid);
+       }
 
        /**
         * Deletes all relations of an object.
@@ -691,7 +703,7 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         * @param array $relations The queued relations
         * @return void
         */
-       protected function deleteRelations(Tx_Extbase_DomainObject_DomainObjectInterface $object, $propertyName, array $relations) {
+       protected function deleteRelatedObjects(Tx_Extbase_DomainObject_DomainObjectInterface $object, array $relations) {
                $dataMap = $this->getDataMap(get_class($object));
                foreach ($relations as $propertyName => $relatedObjects) {
                        if (is_array($relatedObjects)) {
@@ -706,30 +718,6 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
        }
 
        /**
-        * Inserts relation to 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 insertRelationInRelationTable(Tx_Extbase_DomainObject_DomainObjectInterface $relatedObject, Tx_Extbase_DomainObject_DomainObjectInterface $parentObject, $parentPropertyName) {
-               $dataMap = $this->getDataMap(get_class($parentObject));
-               $rowToInsert = array(
-                       'uid_local' => $parentObject->getUid(),
-                       'uid_foreign' => $relatedObject->getUid(),
-                       'tablenames' => $dataMap->getTableName(),
-                       'sorting' => 9999 // TODO sorting of mm table items
-                       );
-               $tableName = $dataMap->getColumnMap($parentPropertyName)->getRelationTableName();
-               $res = $this->database->exec_INSERTquery(
-                       $tableName,
-                       $rowToInsert
-                       );
-               $this->statistics['insert']++;
-       }
-
-       /**
         * Update relations in a relation table
         *
         * @param array $relatedObjects An array of related objects
@@ -745,7 +733,6 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                        $tableName,
                        'uid_local=' . $parentObject->getUid()
                        );
-               $this->statistics['select']++;
                $existingRelations = array();
                while($row = $this->database->sql_fetch_assoc($res)) {
                        $existingRelations[current($row)] = current($row);
@@ -765,7 +752,6 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
                                $tableName,
                                'uid_local=' . $parentObject->getUid() . ' AND uid_foreign IN (' . $relationsToDeleteList . ')'
                                );
-                       $this->statistics['delete']++;
                }
        }
                
@@ -789,14 +775,12 @@ class Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper implements t3lib_Sing
         */
        protected function getDataMap($className) {
                if (empty($this->dataMaps[$className])) {
+                       // TODO Inject DataMap
                        $dataMap = new Tx_Extbase_Persistence_Mapper_DataMap($className);
                        $this->dataMaps[$className] = $dataMap;
                }
                return $this->dataMaps[$className];
        }
-       
-       public function getStatistics() {
-               return $this->statistics;
-       }
+
 }
 ?>
\ No newline at end of file
index c57e3c3..be0ce42 100644 (file)
@@ -100,12 +100,12 @@ class Tx_Extbase_Persistence_ObjectStorage implements Iterator, Countable, Array
         * @param string $obj The object
         * @return void
         */
-       public function offsetSet($offset, $obj) {
+       public function offsetSet($offset, $value) {
                if (!is_object($offset)) throw new Tx_Extbase_Exception_InvalidArgumentType('Expected parameter 1 to be object, ' . gettype($offset) . ' given');
-               if (!is_object($obj)) throw new Tx_Extbase_Exception_InvalidArgumentType('Expected parameter 2 to be object, ' . gettype($offset) . ' given');
-               if (!($offset === $obj)) throw new Tx_Extbase_Exception_InvalidArgumentType('Parameter 1 and parameter 2 must be a reference to the same object.');
-               if (!$this->contains($obj)) {
-                       $this->storage[spl_object_hash($offset)] = $obj;
+               // if (!is_object($obj)) throw new Tx_Extbase_Exception_InvalidArgumentType('Expected parameter 2 to be object, ' . gettype($offset) . ' given');
+               // if (!($offset === $obj)) throw new Tx_Extbase_Exception_InvalidArgumentType('Parameter 1 and parameter 2 must be a reference to the same object.');
+               if (!$this->contains($offset)) {
+                       $this->storage[spl_object_hash($offset)] = $value;
                }
        }
 
@@ -159,10 +159,13 @@ class Tx_Extbase_Persistence_ObjectStorage implements Iterator, Countable, Array
         * @param Object $obj The Object to be attached
         * @return void
         */
-       public function attach($object) {
+       public function attach($object, $value = NULL) {
                if (!is_object($object)) throw new Tx_Extbase_Exception_InvalidArgumentType('Expected parameter to be an object, ' . gettype($object) . ' given');
                if (!$this->contains($object)) {
-                       $this->storage[spl_object_hash($object)] = $object;
+                       if ($value === NULL) {
+                               $value = $object;
+                       }
+                       $this->storage[spl_object_hash($object)] = $value;
                }
        }
 
@@ -178,6 +181,35 @@ class Tx_Extbase_Persistence_ObjectStorage implements Iterator, Countable, Array
        }
 
        /**
+        * Attach all objects to the storage
+        *
+        * @param array $objects The objects to be attached to the storage
+        * @return void
+        */
+       public function addAll($objects) {
+               if (is_array($objects) || ($objects instanceof Tx_Extbase_Persistence_ObjectStorage)) {
+                       foreach ($objects as $object) {
+                               $this->attach($object);
+                       }
+               } else {
+                throw new Tx_Extbase_Exception_InvalidArgumentType('Expected parameter to be an array, ' . gettype($object) . ' given');
+               }
+       }
+
+       /**
+        * Detaches all objects from the storage
+        *
+        * @param array $objects The objects to be detached from the storage
+        * @return void
+        */
+       public function removeAll($objects) {
+               if (!is_array($object)) throw new Tx_Extbase_Exception_InvalidArgumentType('Expected parameter to be an array, ' . gettype($object) . ' given');
+               foreach ($objects as $object) {
+                       $this->detach($object);
+               }
+       }
+
+       /**
         * Returns this object storage as an array
         *
         * @return array The object storage
index f1e8d03..84f26b1 100644 (file)
@@ -42,13 +42,6 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
        protected $aggregateRootClassName;
 
        /**
-        * Objects of this repository
-        *
-        * @var Tx_Extbase_Persistence_ObjectStorage
-        */
-       protected $objects;
-
-       /**
         * Contains the persistence session of the current extension
         *
         * @var Tx_Extbase_Persistence_Session
@@ -60,7 +53,6 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
         *
         */
        public function __construct($aggregateRootClassName = NULL) {
-               $this->objects = new Tx_Extbase_Persistence_ObjectStorage();
                $repositoryClassName = get_class($this);
                $repositoryPosition = strrpos($repositoryClassName, 'Repository');
                if ($aggregateRootClassName != NULL) {
@@ -76,7 +68,6 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
                }
                $this->dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper'); // singleton
                $this->persistenceSession = t3lib_div::makeInstance('Tx_Extbase_Persistence_Session'); // singleton
-               $this->persistenceSession->registerAggregateRootClassName($this->aggregateRootClassName);
        }
 
        /**
@@ -87,7 +78,6 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
         */
        public function add($object) {
                if (!($object instanceof $this->aggregateRootClassName)) throw new Tx_Extbase_Persistence_Exception_InvalidClass('The class "' . get_class($object) . '" is not supported by the repository.');
-               $this->objects->attach($object);
                $this->persistenceSession->registerAddedObject($object);
        }
 
@@ -99,10 +89,35 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
         */
        public function remove($object) {
                if (!($object instanceof $this->aggregateRootClassName)) throw new Tx_Extbase_Persistence_Exception_InvalidClass('The class "' . get_class($object) . '" is not supported by the repository.');
-               $this->objects->detach($object);
                $this->persistenceSession->registerRemovedObject($object);
        }
-
+               
+       /**
+        * Replaces an object by another.
+        *
+        * @param object $existingObject The existing object
+        * @param object $newObject The new object
+        */
+       public function replace($existingObject, $newObject) {
+               // TODO Implement replace()
+               // $uid = $dataMapper->getUidByObject($existingObject);
+               // if ($uid !== NULL) {
+               //      // $dataMapper->replaceObject($existingObject, $newObject);
+               //      $this->persistenceSession->unregisterReconstitutedObject($existingObject);
+               //      $this->persistenceSession->registerReconstitutedObject($newObject);
+               // 
+               //      if ($this->persistenceSession->isRemovedObject($existingObject)) {
+               //              $this->persistenceSession->unregisterRemovedObject($existingObject);
+               //              $this->removedObjects->registerRemovedObject($newObject);
+               //      }
+               // } elseif ($this->persistenceSession->isAddedObject($existingObject)) {
+               //      $this->persistenceSession->unregisterAddedObject($existingObject);
+               //      $this->persistenceSession->registerAddedObject($newObject);
+               // } else {
+               //      throw new Tx_Extbase_Persistence_Exception_UnknownObject('The "existing object" is unknown to the persistence backend.', 1238068475);
+               // }
+       }
+       
        /**
         * Dispatches magic methods (findBy[Property]())
         *
@@ -112,7 +127,6 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
         * @return void
         */
        public function __call($methodName, $arguments) {
-               // TODO Forward arguments to find ByConditions 
                if (substr($methodName, 0, 6) === 'findBy' && strlen($methodName) > 7) {
                        $propertyName = Tx_Extbase_Utility_Strings::lowercaseFirst(substr($methodName,6));
                        return $this->findByConditions(array($propertyName => $arguments[0]));
@@ -138,8 +152,10 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
         * @param bool $useEnableFields Wether to automatically restrict the query by enable fields
         * @return array An array of objects, an empty array if no objects found
         */
-       public function findWhere($where = '', $groupBy = '', $orderBy = '', $limit = '', $useEnableFields = TRUE) {            
-               return $this->dataMapper->fetch($this->aggregateRootClassName, $where, '', $groupBy, $orderBy, $limit, $useEnableFields);
+       public function findWhere($where = '', $groupBy = '', $orderBy = '', $limit = '', $useEnableFields = TRUE) {
+               $objects = $this->dataMapper->fetch($this->aggregateRootClassName, $where, '', $groupBy, $orderBy, $limit, $useEnableFields);
+               $this->persistenceSession->registerReconstitutedObjects($objects);
+               return $objects;
        }
 
        /**
@@ -167,7 +183,9 @@ abstract class Tx_Extbase_Persistence_Repository implements Tx_Extbase_Persisten
         */
        public function findByConditions($conditions = '', $groupBy = '', $orderBy = '', $limit = '', $useEnableFields = TRUE) {
                $where = $this->dataMapper->buildQuery($this->aggregateRootClassName, $conditions);
-               return $this->dataMapper->fetch($this->aggregateRootClassName, $where, '', $groupBy, $orderBy, $limit, $useEnableFields);
+               $objects = $this->dataMapper->fetch($this->aggregateRootClassName, $where, '', $groupBy, $orderBy, $limit, $useEnableFields);
+               $this->persistenceSession->registerReconstitutedObjects($objects);
+               return $objects;
        }
 
        /**
index 773256e..64b3d16 100644 (file)
@@ -48,6 +48,14 @@ interface Tx_Extbase_Persistence_RepositoryInterface {
        public function remove($object);
        
        /**
+        * Replaces an object by another.
+        *
+        * @param object $existingObject The existing object
+        * @param object $newObject The new object
+        */
+       public function replace($existingObject, $newObject);
+       
+       /**
         * Returns all objects of this repository.
         *
         * @return array An array of objects, empty if no objects found
index c00a59f..6f081b1 100644 (file)
@@ -100,16 +100,10 @@ class Tx_Extbase_Persistence_Session implements t3lib_singleton {
        /**
         * Returns all objects which have been registered as added objects
         *
-        * @param string $objectClassName The class name of objects to be returned
         * @return array All added objects
         */
-       public function getAddedObjects($objectClassName = NULL) {
-               $addedObjects = array();
-               foreach ($this->addedObjects as $object) {
-                       if ($objectClassName != NULL && !($object instanceof $objectClassName)) continue;
-                       $addedObjects[] = $object;
-               }
-               return $addedObjects;
+       public function getAddedObjects() {
+               return $this->addedObjects;
        }
 
        /**
@@ -149,16 +143,10 @@ class Tx_Extbase_Persistence_Session implements t3lib_singleton {
        /**
         * Returns all objects which have been registered as removed objects
         *
-        * @param string $objectClassName The class name of objects to be returned
         * @return array All removed objects
         */
-       public function getRemovedObjects($objectClassName = NULL) {
-               $removedObjects = array();
-               foreach ($this->removedObjects as $object) {
-                       if ($objectClassName != NULL && !($object instanceof $objectClassName)) continue;
-                       $removedObjects[] = $object;
-               }
-               return $removedObjects;
+       public function getRemovedObjects() {
+               return $this->removedObjects;
        }
 
        /**
@@ -172,15 +160,26 @@ class Tx_Extbase_Persistence_Session implements t3lib_singleton {
        }
 
        /**
+        * Registers all given objects as reconstituted
+        *
+        * @param array $objects
+        * @return void
+        */
+       public function registerReconstitutedObjects(array $objects) {
+               foreach ($objects as $object) {
+                       $this->registerReconstitutedObject($object);
+               }
+       }
+
+       /**
         * Registers a reconstituted object
         *
         * @param object $object
-        * @return Tx_Extbase_DomainObject_DomainObjectInterface
+        * @return void
         */
        public function registerReconstitutedObject(Tx_Extbase_DomainObject_DomainObjectInterface $object) {
                if ($this->addedObjects->contains($object)) throw new InvalidArgumentException('The object was registered as added and can therefore not be registered as reconstituted.');
                $this->reconstitutedObjects->attach($object);
-               $object->_memorizeCleanState();
        }
 
        /**
@@ -199,13 +198,8 @@ class Tx_Extbase_Persistence_Session implements t3lib_singleton {
         * @param string $objectClassName The class name of objects to be returned
         * @return array All reconstituted objects
         */
-       public function getReconstitutedObjects($objectClassName = NULL) {
-               $reconstitutedObjects = array();
-               foreach ($this->reconstitutedObjects as $object) {
-                       if ($objectClassName != NULL && !($object instanceof $objectClassName)) continue;
-                       $reconstitutedObjects[] = $object;
-               }
-               return $reconstitutedObjects;
+       public function getReconstitutedObjects() {
+               return $this->reconstitutedObjects;
        }
 
        /**
@@ -221,15 +215,13 @@ class Tx_Extbase_Persistence_Session implements t3lib_singleton {
        /**
         * Returns all objects marked as dirty (changed after reconstitution)
         *
-        * @param string $objectClassName The class name of objects to be returned
         * @return array An array of dirty objects
         */
-       public function getDirtyObjects($objectClassName = NULL) {
-               $dirtyObjects = array();
+       public function getDirtyObjects() {
+               $dirtyObjects = new Tx_Extbase_Persistence_ObjectStorage();
                foreach ($this->reconstitutedObjects as $object) {
-                       if ($objectClassName != NULL && !($object instanceof $objectClassName)) continue;
                        if ($object->_isDirty()) {
-                               $dirtyObjects[] = $object;
+                               $dirtyObjects->attach($object);
                        }
                }
                return $dirtyObjects;
@@ -294,8 +286,26 @@ class Tx_Extbase_Persistence_Session implements t3lib_singleton {
         * @return void
         */
        public function commit() {
+               $aggregateRootObjects = new Tx_Extbase_Persistence_ObjectStorage();
+               $aggregateRootObjects->addAll($this->getAddedObjects());
+               $aggregateRootObjects->addAll($this->getReconstitutedObjects());
+
+               // hand in only aggregate roots, leaving handling of subobjects to
+               // the underlying storage layer
                $dataMapper = t3lib_div::makeInstance('Tx_Extbase_Persistence_Mapper_ObjectRelationalMapper'); // singleton
-               $dataMapper->persistAll();
+               $dataMapper->setAggregateRootObjects($aggregateRootObjects);
+               $dataMapper->setDeletedObjects($this->getRemovedObjects());
+               $dataMapper->persistObjects();
+               $dataMapper->processDeletedObjects();
+
+               // this needs to unregister more than just those, as at least some of
+               // the subobjects are supposed to go away as well...
+               // OTOH those do no harm, changes to the unused ones should not happen,
+               // so all they do is eat some memory.
+               foreach($this->getRemovedObjects() as $removedObject) {
+                       $this->unregisterObject($removedObject);
+               }
+               
        }
 
 }