[!!!][BUGFIX] Introduce explicit saving of modified domain objects
authorThomas Maroschik <tmaroschik@dfau.de>
Mon, 15 Apr 2013 15:48:35 +0000 (17:48 +0200)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Thu, 18 Apr 2013 15:07:12 +0000 (17:07 +0200)
In conjunction with the new property mapper the persistence layer
saves modified domain objects that failed validation and/or mapping.

This bugfix aligns the behavior of the persistence layer with Flow
and introduces explicit saving of modified domain objects.

Modified objects now need to be passed through their repository
update method in order to be scheduled for saving by the persistence
managers persistAll method.

Fixes: #47251
Releases: 6.1
Change-Id: I2130f96d925d74942af70f92e38d2d0b2ea46f79
Reviewed-on: https://review.typo3.org/19992
Reviewed-by: Stefan Neufeind
Tested-by: Stefan Neufeind
Reviewed-by: Stefan Froemken
Tested-by: Stefan Froemken
Reviewed-by: Anja Leichsenring
Tested-by: Anja Leichsenring
14 files changed:
typo3/sysext/extbase/Classes/Persistence/Generic/Backend.php
typo3/sysext/extbase/Classes/Persistence/Generic/BackendInterface.php
typo3/sysext/extbase/Classes/Persistence/Generic/IdentityMap.php
typo3/sysext/extbase/Classes/Persistence/Generic/Mapper/DataMapper.php
typo3/sysext/extbase/Classes/Persistence/Generic/PersistenceManager.php
typo3/sysext/extbase/Classes/Persistence/Generic/Session.php
typo3/sysext/extbase/Classes/Persistence/PersistenceManagerInterface.php
typo3/sysext/extbase/Classes/Persistence/Repository.php
typo3/sysext/extbase/Classes/Persistence/RepositoryInterface.php
typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/Entity2.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/Entity3.php [new file with mode: 0644]
typo3/sysext/extbase/Tests/Unit/Persistence/Generic/PersistenceManagerTest.php
typo3/sysext/extbase/Tests/Unit/Persistence/Generic/SessionTest.php
typo3/sysext/extbase/Tests/Unit/Persistence/RepositoryTest.php

index 5c09b86..3eeeb71 100644 (file)
@@ -39,6 +39,11 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        protected $session;
 
        /**
+        * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
+        */
+       protected $persistenceManager;
+
+       /**
         * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
         */
        protected $aggregateRootObjects;
@@ -46,22 +51,22 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        /**
         * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
         */
-       protected $visitedDuringPersistence;
+       protected $deletedEntities;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap
+        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
         */
-       protected $identityMap;
+       protected $changedEntities;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
+        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
         */
-       protected $reflectionService;
+       protected $visitedDuringPersistence;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface
+        * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
         */
-       protected $queryFactory;
+       protected $reflectionService;
 
        /**
         * @var \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory
@@ -96,11 +101,6 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        protected $signalSlotDispatcher;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
-        */
-       protected $deletedObjects;
-
-       /**
         * Constructs the backend
         *
         * @param \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager
@@ -109,7 +109,9 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        public function __construct(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface $configurationManager) {
                $this->configurationManager = $configurationManager;
                $this->referenceIndex = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\ReferenceIndex');
-               $this->deletedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->aggregateRootObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->changedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
        }
 
        /**
@@ -139,16 +141,6 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        }
 
        /**
-        * Injects the identity map
-        *
-        * @param \TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap $identityMap
-        * @return void
-        */
-       public function injectIdentityMap(\TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap $identityMap) {
-               $this->identityMap = $identityMap;
-       }
-
-       /**
         * Injects the Reflection Service
         *
         * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService
@@ -159,16 +151,6 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        }
 
        /**
-        * Injects the QueryFactory
-        *
-        * @param \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface $queryFactory
-        * @return void
-        */
-       public function injectQueryFactory(\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface $queryFactory) {
-               $this->queryFactory = $queryFactory;
-       }
-
-       /**
         * Injects the QueryObjectModelFactory
         *
         * @param \TYPO3\CMS\Extbase\Persistence\Generic\Qom\QueryObjectModelFactory $qomFactory
@@ -186,6 +168,13 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        }
 
        /**
+        * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
+        */
+       public function setPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager) {
+               $this->persistenceManager = $persistenceManager;
+       }
+
+       /**
         * Returns the repository session
         *
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\Session
@@ -213,15 +202,6 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        }
 
        /**
-        * Returns the current identityMap
-        *
-        * @return \TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap
-        */
-       public function getIdentityMap() {
-               return $this->identityMap;
-       }
-
-       /**
         * Returns the reflection service
         *
         * @return \TYPO3\CMS\Extbase\Reflection\ReflectionService
@@ -266,8 +246,8 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
                                return NULL;
                        }
                }
-               if ($this->identityMap->hasObject($object)) {
-                       return $this->identityMap->getIdentifierByObject($object);
+               if ($this->session->hasObject($object)) {
+                       return $this->session->getIdentifierByObject($object);
                } else {
                        return NULL;
                }
@@ -282,10 +262,10 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         * @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);
+               if ($this->session->hasIdentifier($identifier, $className)) {
+                       return $this->session->getObjectByIdentifier($identifier, $className);
                } else {
-                       $query = $this->queryFactory->create($className);
+                       $query = $this->persistenceManager->createQueryForType($className);
                        $query->getQuerySettings()->setRespectStoragePage(FALSE);
                        return $query->matching($query->equals('uid', $identifier))->execute()->getFirst();
                }
@@ -316,14 +296,10 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         * @param object $newObject The new object
         * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
         * @return void
+        * @deprecated since 6.1, will be removed two versions later
         */
        public function replaceObject($existingObject, $newObject) {
-               $existingUid = $this->getIdentifierByObject($existingObject);
-               if ($existingUid === NULL) {
-                       throw new \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException('The given object is unknown to this persistence backend.', 1238070163);
-               }
-               $this->identityMap->unregisterObject($existingObject);
-               $this->identityMap->registerObject($newObject, $existingUid);
+               $this->session->replaceReconstitutedEntity($existingObject, $newObject);
        }
 
        /**
@@ -337,13 +313,23 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        }
 
        /**
+        * Sets the changed objects
+        *
+        * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
+        * @return void
+        */
+       public function setChangedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities) {
+               $this->changedEntities = $entities;
+       }
+
+       /**
         * Sets the deleted objects
         *
-        * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
+        * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
         * @return void
         */
-       public function setDeletedObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects) {
-               $this->deletedObjects = $objects;
+       public function setDeletedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities) {
+               $this->deletedEntities = $entities;
        }
 
        /**
@@ -357,6 +343,17 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        }
 
        /**
+        * Sets the deleted objects
+        *
+        * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
+        * @return void
+        * @deprecated since 6.1, will be removed two versions later
+        */
+       public function setDeletedObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects) {
+               $this->setDeletedEntities($objects);
+       }
+
+       /**
         * Traverse and persist all aggregate roots and their object graph.
         *
         * @return void
@@ -364,12 +361,10 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        protected function persistObjects() {
                $this->visitedDuringPersistence = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
                foreach ($this->aggregateRootObjects as $object) {
-                       if (!$this->identityMap->hasObject($object)) {
-                               $this->insertObject($object);
-                       }
+                       $this->persistObject($object, NULL);
                }
-               foreach ($this->aggregateRootObjects as $object) {
-                       $this->persistObject($object);
+               foreach ($this->changedEntities as $object) {
+                       $this->persistObject($object, NULL);
                }
        }
 
@@ -464,7 +459,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
                foreach ($this->getRemovedChildObjects($parentObject, $propertyName) as $removedObject) {
                        $this->detachObjectFromParentObject($removedObject, $parentObject, $propertyName);
                        if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY && $propertyMetaData['cascade'] === 'remove') {
-                               $this->removeObject($removedObject);
+                               $this->removeEntity($removedObject);
                        }
                }
 
@@ -669,7 +664,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
                if ($frameworkConfiguration['persistence']['updateReferenceIndex'] === '1') {
                        $this->referenceIndex->updateRefIndexTable($dataMap->getTableName(), $uid);
                }
-               $this->identityMap->registerObject($object, $uid);
+               $this->session->registerObject($object, $uid);
        }
 
        /**
@@ -922,11 +917,14 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         * @return void
         */
        protected function processDeletedObjects() {
-               foreach ($this->deletedObjects as $object) {
-                       $this->removeObject($object);
-                       $this->identityMap->unregisterObject($object);
+               foreach ($this->deletedEntities as $entity) {
+                       if ($this->session->hasObject($entity)) {
+                               $this->removeEntity($entity);
+                               $this->session->unregisterReconstitutedEntity($entity);
+                               $this->session->unregisterObject($entity);
+                       }
                }
-               $this->deletedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->deletedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
        }
 
        /**
@@ -936,7 +934,7 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
         * @param bool $markAsDeleted Wether to just flag the row deleted (default) or really delete it
         * @return void
         */
-       protected function removeObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $markAsDeleted = TRUE) {
+       protected function removeEntity(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object, $markAsDeleted = TRUE) {
                $dataMap = $this->dataMapper->getDataMap(get_class($object));
                $tableName = $dataMap->getTableName();
                if ($markAsDeleted === TRUE && $dataMap->getDeletedFlagColumnName() !== NULL) {
@@ -977,10 +975,10 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
                        if ($propertyMetaData['cascade'] === 'remove') {
                                if ($columnMap->getTypeOfRelation() === \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\ColumnMap::RELATION_HAS_MANY) {
                                        foreach ($propertyValue as $containedObject) {
-                                               $this->removeObject($containedObject);
+                                               $this->removeEntity($containedObject);
                                        }
                                } elseif ($propertyValue instanceof \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface) {
-                                       $this->removeObject($propertyValue);
+                                       $this->removeEntity($propertyValue);
                                }
                        }
                }
@@ -1034,4 +1032,4 @@ class Backend implements \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
        }
 }
 
-?>
+?>
\ No newline at end of file
index e7c4f2a..02f8d88 100644 (file)
@@ -33,6 +33,14 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic;
 interface BackendInterface {
 
        /**
+        * Set a PersistenceManager instance.
+        *
+        * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
+        * @return void
+        */
+       public function setPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager);
+
+       /**
         * Sets the aggregate root objects
         *
         * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
@@ -41,12 +49,21 @@ interface BackendInterface {
        public function setAggregateRootObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects);
 
        /**
-        * Sets the deleted objects
+        * Sets the deleted entities
         *
-        * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects
+        * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
+        * @return void
+        * @api
+        */
+       public function setDeletedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities);
+
+       /**
+        * Sets the changed objects
+        *
+        * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities
         * @return void
         */
-       public function setDeletedObjects(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $objects);
+       public function setChangedEntities(\TYPO3\CMS\Extbase\Persistence\ObjectStorage $entities);
 
        /**
         * Commits the current persistence session
@@ -55,6 +72,8 @@ interface BackendInterface {
         */
        public function commit();
 
+       // TODO refactor towards being closer to the Flow backend interface again
+
        /**
         * Returns the (internal) identifier for the object, if it is known to the
         * backend. Otherwise NULL is returned.
@@ -82,22 +101,6 @@ interface BackendInterface {
         */
        public function isNewObject($object);
 
-       /**
-        * Replaces the given object by the second object.
-        *
-        * This method will unregister the existing object at the identity map and
-        * register the new object instead. The existing object must therefore
-        * already be registered at the identity map which is the case for all
-        * reconstituted objects.
-        *
-        * The new object will be identified by the uuid which formerly belonged
-        * to the existing object. The existing object looses its uuid.
-        *
-        * @param object $existingObject The existing object
-        * @param object $newObject The new object
-        * @return void
-        */
-       public function replaceObject($existingObject, $newObject);
 }
 
 ?>
\ No newline at end of file
index 2055a47..8de6cd8 100644 (file)
@@ -33,25 +33,20 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic;
  * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License, version 3 or later
  * @see \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper
  * @see \TYPO3\CMS\Extbase\Persistence\Generic\Backend
+ * @deprecated since 6.1, will be removed two versions later, use the persistence session instead
  */
 class IdentityMap implements \TYPO3\CMS\Core\SingletonInterface {
 
        /**
-        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
+        * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
         */
-       protected $objectMap;
+       protected $persistenceSession;
 
        /**
-        * @var array
+        * @param \TYPO3\CMS\Extbase\Persistence\Generic\Session
         */
-       protected $uuidMap = array();
-
-       /**
-        * Constructs a new Identity Map
-        *
-        */
-       public function __construct() {
-               $this->objectMap = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+       public function injectPersistenceSession(\TYPO3\CMS\Extbase\Persistence\Generic\Session $persistenceSession) {
+               $this->persistenceSession = $persistenceSession;
        }
 
        /**
@@ -59,9 +54,10 @@ class IdentityMap implements \TYPO3\CMS\Core\SingletonInterface {
         *
         * @param object $object
         * @return boolean
+        * @deprecated since 6.1, will be removed two versions later, use the persistence session instead
         */
        public function hasObject($object) {
-               return $this->objectMap->contains($object);
+               return $this->persistenceSession->hasObject($object);
        }
 
        /**
@@ -70,13 +66,10 @@ class IdentityMap implements \TYPO3\CMS\Core\SingletonInterface {
         * @param string $uuid
         * @param string $className
         * @return boolean
+        * @deprecated since 6.1, will be removed two versions later, use the persistence session instead
         */
        public function hasIdentifier($uuid, $className) {
-               if (is_array($this->uuidMap[$className])) {
-                       return array_key_exists($uuid, $this->uuidMap[$className]);
-               } else {
-                       return FALSE;
-               }
+               return $this->persistenceSession->hasIdentifier($uuid, $className);
        }
 
        /**
@@ -85,9 +78,10 @@ class IdentityMap implements \TYPO3\CMS\Core\SingletonInterface {
         * @param string $uuid
         * @param string $className
         * @return object
+        * @deprecated since 6.1, will be removed two versions later, use the persistence session instead
         */
        public function getObjectByIdentifier($uuid, $className) {
-               return $this->uuidMap[$className][$uuid];
+               return $this->persistenceSession->getObjectByIdentifier($uuid, $className);
        }
 
        /**
@@ -97,15 +91,10 @@ class IdentityMap implements \TYPO3\CMS\Core\SingletonInterface {
         * @throws \InvalidArgumentException
         * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
         * @return string
+        * @deprecated since 6.1, will be removed two versions later, use the persistence session instead
         */
        public function getIdentifierByObject($object) {
-               if (!is_object($object)) {
-                       throw new \InvalidArgumentException('Object expected, ' . gettype($object) . ' given.', 1246892972);
-               }
-               if (!isset($this->objectMap[$object])) {
-                       throw new \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException('The given object (class: ' . get_class($object) . ') is not registered in this Identity Map.', 1246892970);
-               }
-               return $this->objectMap[$object];
+               return $this->persistenceSession->getIdentifierByObject($object);
        }
 
        /**
@@ -113,10 +102,10 @@ class IdentityMap implements \TYPO3\CMS\Core\SingletonInterface {
         *
         * @param object $object
         * @param string $uuid
+        * @deprecated since 6.1, will be removed two versions later, use the persistence session instead
         */
        public function registerObject($object, $uuid) {
-               $this->objectMap[$object] = $uuid;
-               $this->uuidMap[get_class($object)][$uuid] = $object;
+               $this->persistenceSession->registerObject($object, $uuid);
        }
 
        /**
@@ -124,10 +113,10 @@ class IdentityMap implements \TYPO3\CMS\Core\SingletonInterface {
         *
         * @param object $object
         * @return void
+        * @deprecated since 6.1, will be removed two versions later, use the persistence session instead
         */
        public function unregisterObject($object) {
-               unset($this->uuidMap[get_class($object)][$this->objectMap[$object]]);
-               $this->objectMap->detach($object);
+               $this->persistenceSession->unregisterObject($object);
        }
 }
 
index aa28b3e..66a80bb 100644 (file)
@@ -207,7 +207,7 @@ class DataMapper implements \TYPO3\CMS\Core\SingletonInterface {
                        $this->identityMap->registerObject($object, $row['uid']);
                        $this->thawProperties($object, $row);
                        $object->_memorizeCleanState();
-                       $this->persistenceSession->registerReconstitutedObject($object);
+                       $this->persistenceSession->registerReconstitutedEntity($object);
                }
                return $object;
        }
index d7ffec2..9606334 100644 (file)
@@ -35,26 +35,48 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic;
 class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface, \TYPO3\CMS\Core\SingletonInterface {
 
        /**
-        * @var \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
+        * @var array
         */
-       protected $backend;
+       protected $newObjects = array();
 
        /**
-        * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
+        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
         */
-       protected $session;
+       protected $changedObjects;
 
        /**
-        * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
+        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
         */
-       protected $objectManager;
+       protected $addedObjects;
 
        /**
-        * This is an array of registered repository class names.
-        *
-        * @var array
+        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
+        */
+       protected $removedObjects;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface
+        */
+       protected $queryFactory;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Persistence\Generic\Backend
         */
-       protected $repositoryClassNames = array();
+       protected $backend;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
+        */
+       protected $persistenceSession;
+
+       /**
+        * Create new instance
+        */
+       public function __construct() {
+               $this->addedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->removedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->changedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+       }
 
        /**
         * Injects the Persistence Backend
@@ -67,33 +89,33 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
        }
 
        /**
-        * Injects the Persistence Session
+        * Injects a QueryFactory instance
         *
-        * @param \TYPO3\CMS\Extbase\Persistence\Generic\Session $session The persistence session
+        * @param \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface $queryFactory
         * @return void
         */
-       public function injectSession(\TYPO3\CMS\Extbase\Persistence\Generic\Session $session) {
-               $this->session = $session;
+       public function injectQueryFactory(\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface $queryFactory) {
+               $this->queryFactory = $queryFactory;
        }
 
        /**
-        * Injects the object manager
+        * Injects the Persistence Session
         *
-        * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
+        * @param \TYPO3\CMS\Extbase\Persistence\Generic\Session $session The persistence session
         * @return void
         */
-       public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) {
-               $this->objectManager = $objectManager;
+       public function injectPersistenceSession(\TYPO3\CMS\Extbase\Persistence\Generic\Session $session) {
+               $this->persistenceSession = $session;
        }
 
        /**
         * Registers a repository
         *
         * @param string $className The class name of the repository to be reigistered
+        * @deprecated since 6.1, will be remove two versions later
         * @return void
         */
        public function registerRepositoryClassName($className) {
-               $this->repositoryClassNames[] = $className;
        }
 
        /**
@@ -131,7 +153,7 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         * @api
         */
        public function getIdentifierByObject($object) {
-               return $this->backend->getIdentifierByObject($object);
+               return $this->persistenceSession->getIdentifierByObject($object);
        }
 
        /**
@@ -145,7 +167,14 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         * @api
         */
        public function getObjectByIdentifier($identifier, $objectType = NULL, $useLazyLoading = FALSE) {
-               return $this->backend->getObjectByIdentifier($identifier, $objectType);
+               if (isset($this->newObjects[$identifier])) {
+                       return $this->newObjects[$identifier];
+               }
+               if ($this->persistenceSession->hasIdentifier($identifier, $objectType)) {
+                       return $this->persistenceSession->getObjectByIdentifier($identifier, $objectType);
+               } else {
+                       return $this->backend->getObjectByIdentifier($identifier, $objectType);
+               }
        }
 
        /**
@@ -156,34 +185,28 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         * @api
         */
        public function persistAll() {
-               $aggregateRootObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
-               $removedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
-               // fetch and inspect objects from all known repositories
-               foreach ($this->repositoryClassNames as $repositoryClassName) {
-                       $repository = $this->objectManager->get($repositoryClassName);
-                       $aggregateRootObjects->addAll($repository->getAddedObjects());
-                       $removedObjects->addAll($repository->getRemovedObjects());
-               }
-               foreach ($this->session->getReconstitutedObjects() as $reconstitutedObject) {
-                       $className = get_class($reconstitutedObject);
-                       $delimiter = strpos($className, '_') !== FALSE ? '_' : '\\';
-                       $possibleRepositoryClassName = str_replace($delimiter . 'Model' . $delimiter, $delimiter . 'Repository' . $delimiter, $className) . 'Repository';
-                       if (class_exists($possibleRepositoryClassName)) {
-                               $aggregateRootObjects->attach($reconstitutedObject);
-                       }
-               }
                // hand in only aggregate roots, leaving handling of subobjects to
                // the underlying storage layer
-               $this->backend->setAggregateRootObjects($aggregateRootObjects);
-               $this->backend->setDeletedObjects($removedObjects);
+               // reconstituted entities must be fetched from the session and checked
+               // for changes by the underlying backend as well!
+               $this->backend->setAggregateRootObjects($this->addedObjects);
+               $this->backend->setChangedEntities($this->changedObjects);
+               $this->backend->setDeletedEntities($this->removedObjects);
                $this->backend->commit();
-               // 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 ($removedObjects as $removedObject) {
-                       $this->session->unregisterReconstitutedObject($removedObject);
-               }
+
+               $this->addedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->removedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->changedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+       }
+
+       /**
+        * Return a query object for the given type.
+        *
+        * @param string $type
+        * @return \TYPO3\CMS\Extbase\Persistence\QueryInterface
+        */
+       public function createQueryForType($type) {
+               return $this->queryFactory->create($type);
        }
 
        /**
@@ -191,11 +214,11 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         *
         * @param object $object The object to add
         * @return void
-        * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
         * @api
         */
        public function add($object) {
-               throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
+               $this->addedObjects->attach($object);
+               $this->removedObjects->detach($object);
        }
 
        /**
@@ -203,11 +226,14 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         *
         * @param object $object The object to remove
         * @return void
-        * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
         * @api
         */
        public function remove($object) {
-               throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
+               if ($this->addedObjects->contains($object)) {
+                       $this->addedObjects->detach($object);
+               } else {
+                       $this->removedObjects->attach($object);
+               }
        }
 
        /**
@@ -215,12 +241,14 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         *
         * @param object $object The modified object
         * @return void
-        * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
         * @throws \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
         * @api
         */
        public function update($object) {
-               throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
+               if ($this->isNewObject($object)) {
+                       throw new \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException('The object of type "' . get_class($object) . '" given to update must be persisted already, but is new.', 1249479819);
+               }
+               $this->changedObjects->attach($object);
        }
 
        /**
@@ -239,11 +267,9 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         * Initializes the persistence manager, called by Extbase.
         *
         * @return void
-        * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
-        * @api
         */
-       public function initialize() {
-               throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
+       public function initializeObject() {
+               $this->backend->setPersistenceManager($this);
        }
 
        /**
@@ -256,18 +282,39 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
         * @return void
         */
        public function clearState() {
-               throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
+               $this->newObjects = array();
+               $this->addedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->removedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->changedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->persistenceSession->destroy();
        }
 
        /**
         * Checks if the given object has ever been persisted.
         *
         * @param object $object The object to check
-        * @return boolean TRUE if the object is new, FALSE if the object exists in the repository
+        * @return boolean TRUE if the object is new, FALSE if the object exists in the persistence session
         * @api
         */
        public function isNewObject($object) {
-               return $this->backend->isNewObject($object);
+               return ($this->persistenceSession->hasObject($object) === FALSE);
+       }
+
+       /**
+        * Registers an object which has been created or cloned during this request.
+        *
+        * A "new" object does not necessarily
+        * have to be known by any repository or be persisted in the end.
+        *
+        * Objects registered with this method must be known to the getObjectByIdentifier()
+        * method.
+        *
+        * @param object $object The new object to register
+        * @return void
+        */
+       public function registerNewObject($object) {
+               $identifier = $this->getIdentifierByObject($object);
+               $this->newObjects[$identifier] = $object;
        }
 
        /**
@@ -299,16 +346,19 @@ class PersistenceManager implements \TYPO3\CMS\Extbase\Persistence\PersistenceMa
        }
 
        /**
-        * Return a query object for the given type.
+        * Tear down the persistence
         *
-        * @param string $type
-        * @return \TYPO3\CMS\Extbase\Persistence\QueryInterface
-        * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException
-        * @api
+        * This method is called in functional tests to reset the storage between tests.
+        * The implementation is optional and depends on the underlying persistence backend.
+        *
+        * @return void
         */
-       public function createQueryForType($type) {
-               throw new \TYPO3\CMS\Extbase\Persistence\Generic\Exception\NotImplementedException(__METHOD__);
+       public function tearDown() {
+               if (method_exists($this->backend, 'tearDown')) {
+                       $this->backend->tearDown();
+               }
        }
+
 }
 
 ?>
\ No newline at end of file
index abfecc3..43e4467 100644 (file)
@@ -33,48 +33,201 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic;
 class Session implements \TYPO3\CMS\Core\SingletonInterface {
 
        /**
-        * Objects which were reconstituted. The relevant objects are registered by
-        * the \TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper.
+        * Reconstituted objects
         *
         * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
         */
-       protected $reconstitutedObjects;
+       protected $reconstitutedEntities;
+
+       /**
+        * Reconstituted entity data (effectively their clean state)
+        * Currently unused in Extbase
+        * TODO make use of it in Extbase
+        *
+        * @var array
+        */
+       protected $reconstitutedEntitiesData = array();
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
+        */
+       protected $objectMap;
+
+       /**
+        * @var array
+        */
+       protected $identifierMap = array();
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
+        */
+       protected $reflectionService;
 
        /**
         * Constructs a new Session
         */
        public function __construct() {
-               $this->reconstitutedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->reconstitutedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->objectMap = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+       }
+
+       /**
+        * Injects a Reflection Service instance
+        *
+        * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
+        * @return void
+        */
+       public function injectReflectionService(\TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService) {
+               $this->reflectionService = $reflectionService;
+       }
+
+       /**
+        * Registers data for a reconstituted object.
+        *
+        * $entityData format is described in
+        * "Documentation/PersistenceFramework object data format.txt"
+        *
+        * @param object $entity
+        * @param array $entityData
+        * @return void
+        */
+       public function registerReconstitutedEntity($entity, array $entityData = array()) {
+               $this->reconstitutedEntities->attach($entity);
+               $this->reconstitutedEntitiesData[$entityData['identifier']] = $entityData;
        }
 
        /**
-        * Registers a reconstituted object
+        * Replace a reconstituted object, leaves the clean data unchanged.
         *
-        * @param object|\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object $object
+        * @param object $oldEntity
+        * @param object $newEntity
         * @return void
         */
-       public function registerReconstitutedObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) {
-               $this->reconstitutedObjects->attach($object);
+       public function replaceReconstitutedEntity($oldEntity, $newEntity) {
+               $this->reconstitutedEntities->detach($oldEntity);
+               $this->reconstitutedEntities->attach($newEntity);
+       }
+
+       /**
+        * Unregisters data for a reconstituted object
+        *
+        * @param object $entity
+        * @return void
+        */
+       public function unregisterReconstitutedEntity($entity) {
+               if ($this->reconstitutedEntities->contains($entity)) {
+                       $this->reconstitutedEntities->detach($entity);
+                       unset($this->reconstitutedEntitiesData[$this->getIdentifierByObject($entity)]);
+               }
+       }
+
+       /**
+        * Returns all objects which have been registered as reconstituted
+        *
+        * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage All reconstituted objects
+        */
+       public function getReconstitutedEntities() {
+               return $this->reconstitutedEntities;
+       }
+
+       /**
+        * Tells whether the given object is a reconstituted entity.
+        *
+        * @param object $entity
+        * @return boolean
+        */
+       public function isReconstitutedEntity($entity) {
+               return $this->reconstitutedEntities->contains($entity);
+       }
+
+       // TODO implement the is dirty checking behaviour of the Flow persistence session here
+
+       /**
+        * Checks whether the given object is known to the identity map
+        *
+        * @param object $object
+        * @return boolean
+        * @api
+        */
+       public function hasObject($object) {
+               return $this->objectMap->contains($object);
+       }
+
+       /**
+        * Checks whether the given identifier is known to the identity map
+        *
+        * @param string $identifier
+        * @param string $className
+        * @return boolean
+        */
+       public function hasIdentifier($identifier, $className) {
+               return isset($this->identifierMap[strtolower($className)][$identifier]);
        }
 
        /**
-        * Unregisters a reconstituted object
+        * Returns the object for the given identifier
         *
-        * @param \TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object
+        * @param string $identifier
+        * @param string $className
+        * @return object
+        * @api
+        */
+       public function getObjectByIdentifier($identifier, $className) {
+               return $this->identifierMap[strtolower($className)][$identifier];
+       }
+
+       /**
+        * Returns the identifier for the given object from
+        * the session, if the object was registered.
+        *
+        *
+        * @param object $object
+        * @return string
+        * @api
+        */
+       public function getIdentifierByObject($object) {
+               if ($this->hasObject($object)) {
+                       return $this->objectMap[$object];
+               }
+               return NULL;
+       }
+
+       /**
+        * Register an identifier for an object
+        *
+        * @param object $object
+        * @param string $identifier
+        * @api
+        */
+       public function registerObject($object, $identifier) {
+               $this->objectMap[$object] = $identifier;
+               $this->identifierMap[strtolower(get_class($object))][$identifier] = $object;
+       }
+
+       /**
+        * Unregister an object
+        *
+        * @param string $object
         * @return void
         */
-       public function unregisterReconstitutedObject(\TYPO3\CMS\Extbase\DomainObject\DomainObjectInterface $object) {
-               $this->reconstitutedObjects->detach($object);
+       public function unregisterObject($object) {
+               unset($this->identifierMap[strtolower(get_class($object))][$this->objectMap[$object]]);
+               $this->objectMap->detach($object);
        }
 
        /**
-        * Returns all objects which have been registered as reconstituted objects
+        * Destroy the state of the persistence session and reset
+        * all internal data.
         *
-        * @return array All reconstituted objects
+        * @return void
         */
-       public function getReconstitutedObjects() {
-               return $this->reconstitutedObjects;
+       public function destroy() {
+               $this->identifierMap = array();
+               $this->objectMap = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->reconstitutedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $this->reconstitutedEntitiesData = array();
        }
+
 }
 
 ?>
\ No newline at end of file
index f43e0b3..96ef6ff 100644 (file)
@@ -42,6 +42,27 @@ interface PersistenceManagerInterface {
        public function persistAll();
 
        /**
+        * Clears the in-memory state of the persistence.
+        *
+        * Managed instances become detached, any fetches will
+        * return data directly from the persistence "backend".
+        *
+        * @return void
+        */
+       public function clearState();
+
+       /**
+        * Checks if the given object has ever been persisted.
+        *
+        * @param object $object The object to check
+        * @return boolean TRUE if the object is new, FALSE if the object exists in the repository
+        * @api
+        */
+       public function isNewObject($object);
+
+       // TODO realign with Flow PersistenceManager again
+
+       /**
         * Returns the (internal) identifier for the object, if it is known to the
         * backend. Otherwise NULL is returned.
         *
@@ -134,33 +155,6 @@ interface PersistenceManagerInterface {
        public function injectSettings(array $settings);
 
        /**
-        * Initializes the persistence manager, called by Extbase.
-        *
-        * @return void
-        * @api
-        */
-       public function initialize();
-
-       /**
-        * Clears the in-memory state of the persistence.
-        *
-        * Managed instances become detached, any fetches will
-        * return data directly from the persistence "backend".
-        *
-        * @return void
-        */
-       public function clearState();
-
-       /**
-        * Checks if the given object has ever been persisted.
-        *
-        * @param object $object The object to check
-        * @return boolean TRUE if the object is new, FALSE if the object exists in the repository
-        * @api
-        */
-       public function isNewObject($object);
-
-       /**
         * Converts the given object into an array containing the identity of the domain object.
         *
         * @param object $object The object to be converted
index 89cc889..ba74fb2 100644 (file)
@@ -36,35 +36,19 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
 
        /**
         * @var \TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap
+        * @deprecated since 6.1 will be removed two versions later, use the persistence session instead
         */
        protected $identityMap;
 
        /**
-        * Objects of this repository
-        *
-        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
-        */
-       protected $addedObjects;
-
-       /**
-        * Objects removed but not found in $this->addedObjects at removal time
-        *
-        * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage
-        */
-       protected $removedObjects;
-
-       /**
-        * @var \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactoryInterface
-        */
-       protected $queryFactory;
-
-       /**
         * @var \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
+        * @deprecated since 6.1, will be removed two versions later, use the persistence manager instead
         */
        protected $backend;
 
        /**
         * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
+        * @deprecated since 6.1 will be removed two versions later, use the persistence manager instead
         */
        protected $session;
 
@@ -100,8 +84,6 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         * @deprecated since Extbase 6.0.0; will be removed in Extbase 6.2 - Use objectManager to instantiate repository objects instead of GeneralUtility::makeInstance
         */
        public function __construct(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager = NULL) {
-               $this->addedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
-               $this->removedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
                $nsSeparator = strpos($this->getRepositoryClassName(), '\\') !== FALSE ? '\\\\' : '_';
                $this->objectType = preg_replace(array('/' . $nsSeparator . 'Repository' . $nsSeparator . '(?!.*' . $nsSeparator . 'Repository' . $nsSeparator . ')/', '/Repository$/'), array($nsSeparator . 'Model' . $nsSeparator, ''), $this->getRepositoryClassName());
                if ($objectManager === NULL) {
@@ -112,7 +94,6 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
 
                        $this->objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
                        $this->injectIdentityMap($this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\IdentityMap'));
-                       $this->injectQueryFactory($this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QueryFactory'));
                        $this->injectPersistenceManager($this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\PersistenceManager'));
                        $this->injectBackend($this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\BackendInterface'));
                        $this->injectSession($this->objectManager->get('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Session'));
@@ -124,6 +105,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
        /**
         * @param \TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap $identityMap
         * @return void
+        * @deprecated since 6.1, will be removed two versions later
         */
        public function injectIdentityMap(\TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap $identityMap) {
                $this->identityMap = $identityMap;
@@ -134,6 +116,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         *
         * @param \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface $backend The persistence backend
         * @return void
+        * @deprecated since 6.1, will be removed two versions later
         */
        public function injectBackend(\TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface $backend) {
                $this->backend = $backend;
@@ -145,26 +128,18 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         *
         * @param \TYPO3\CMS\Extbase\Persistence\Generic\Session $session The persistence session
         * @return void
+        * @deprecated since 6.1, will be removed two versions later
         */
        public function injectSession(\TYPO3\CMS\Extbase\Persistence\Generic\Session $session) {
                $this->session = $session;
        }
 
        /**
-        * @param \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory $queryFactory
-        * @return void
-        */
-       public function injectQueryFactory(\TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory $queryFactory) {
-               $this->queryFactory = $queryFactory;
-       }
-
-       /**
         * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager
         * @return void
         */
        public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager) {
                $this->persistenceManager = $persistenceManager;
-               $this->persistenceManager->registerRepositoryClassName($this->getRepositoryClassName());
        }
 
        /**
@@ -179,10 +154,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
                if (!$object instanceof $this->objectType) {
                        throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException('The object given to add() was not of the type (' . $this->objectType . ') this repository manages.', 1248363335);
                }
-               $this->addedObjects->attach($object);
-               if ($this->removedObjects->contains($object)) {
-                       $this->removedObjects->detach($object);
-               }
+               $this->persistenceManager->add($object);
        }
 
        /**
@@ -197,12 +169,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
                if (!$object instanceof $this->objectType) {
                        throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException('The object given to remove() was not of the type (' . $this->objectType . ') this repository manages.', 1248363335);
                }
-               if ($this->addedObjects->contains($object)) {
-                       $this->addedObjects->detach($object);
-               }
-               if (!$object->_isNew()) {
-                       $this->removedObjects->attach($object);
-               }
+               $this->persistenceManager->remove($object);
        }
 
        /**
@@ -210,36 +177,10 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         *
         * @param object $existingObject The existing object
         * @param object $newObject The new object
-        * @throws Exception\UnknownObjectException
-        * @throws Exception\IllegalObjectTypeException
-        * @return void
-        * @api
+        * @deprecated since 6.1, will be removed two versions later
         */
        public function replace($existingObject, $newObject) {
-               if (!$existingObject instanceof $this->objectType) {
-                       throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException('The existing object given to replace was not of the type (' . $this->objectType . ') this repository manages.', 1248363434);
-               }
-               if (!$newObject instanceof $this->objectType) {
-                       throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException('The new object given to replace was not of the type (' . $this->objectType . ') this repository manages.', 1248363439);
-               }
-               $uuid = $this->persistenceManager->getIdentifierByObject($existingObject);
-               if ($uuid !== NULL) {
-                       $this->backend->replaceObject($existingObject, $newObject);
-                       $this->session->unregisterReconstitutedObject($existingObject);
-                       $this->session->registerReconstitutedObject($newObject);
-                       if ($this->removedObjects->contains($existingObject)) {
-                               $this->removedObjects->detach($existingObject);
-                               $this->removedObjects->attach($newObject);
-                       } elseif ($this->addedObjects->contains($existingObject)) {
-                               $this->addedObjects->detach($existingObject);
-                               $this->addedObjects->attach($newObject);
-                       }
-               } elseif ($this->addedObjects->contains($existingObject)) {
-                       $this->addedObjects->detach($existingObject);
-                       $this->addedObjects->attach($newObject);
-               } else {
-                       throw new \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException('The "existing object" is unknown to the persistence backend.', 1238068475);
-               }
+               // Does nothing here as explicit update replaces objects in persistence session already
        }
 
        /**
@@ -255,36 +196,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
                if (!$modifiedObject instanceof $this->objectType) {
                        throw new \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException('The modified object given to update() was not of the type (' . $this->objectType . ') this repository manages.', 1249479625);
                }
-               $uid = $modifiedObject->getUid();
-               if ($uid !== NULL) {
-                       $existingObject = $this->findByUid($uid);
-                       $this->replace($existingObject, $modifiedObject);
-               } else {
-                       throw new \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException('The "modified object" is does not have an existing counterpart in this repository.', 1249479819);
-               }
-       }
-
-       /**
-        * Returns all addedObjects that have been added to this repository with add().
-        *
-        * This is a service method for the persistence manager to get all addedObjects
-        * added to the repository. Those are only objects *added*, not objects
-        * fetched from the underlying storage.
-        *
-        * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage the objects
-        */
-       public function getAddedObjects() {
-               return $this->addedObjects;
-       }
-
-       /**
-        * Returns an \TYPO3\CMS\Extbase\Persistence\ObjectStorage with objects remove()d from the repository
-        * that had been persisted to the storage layer before.
-        *
-        * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage the objects
-        */
-       public function getRemovedObjects() {
-               return $this->removedObjects;
+               $this->persistenceManager->update($modifiedObject);
        }
 
        /**
@@ -294,8 +206,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         * @api
         */
        public function findAll() {
-               $result = $this->createQuery()->execute();
-               return $result;
+               return $this->createQuery()->execute();
        }
 
        /**
@@ -316,8 +227,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         * @api
         */
        public function removeAll() {
-               $this->addedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
-               foreach ($this->findAll() as $object) {
+               foreach ($this->findAll() AS $object) {
                        $this->remove($object);
                }
        }
@@ -330,15 +240,18 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         * @api
         */
        public function findByUid($uid) {
-               if ($this->identityMap->hasIdentifier($uid, $this->objectType)) {
-                       $object = $this->identityMap->getObjectByIdentifier($uid, $this->objectType);
-               } else {
-                       $query = $this->createQuery();
-                       $query->getQuerySettings()->setRespectSysLanguage(FALSE);
-                       $query->getQuerySettings()->setRespectStoragePage(FALSE);
-                       $object = $query->matching($query->equals('uid', $uid))->execute()->getFirst();
-               }
-               return $object;
+               return $this->persistenceManager->getObjectByIdentifier($uid, $this->objectType);
+       }
+
+       /**
+        * Finds an object matching the given identifier.
+        *
+        * @param mixed $identifier The identifier of the object to find
+        * @return object The matching object if found, otherwise NULL
+        * @api
+        */
+       public function findByIdentifier($identifier) {
+               return $this->persistenceManager->getObjectByIdentifier($identifier, $this->objectType);
        }
 
        /**
@@ -375,7 +288,7 @@ class Repository implements \TYPO3\CMS\Extbase\Persistence\RepositoryInterface,
         * @api
         */
        public function createQuery() {
-               $query = $this->queryFactory->create($this->objectType);
+               $query = $this->persistenceManager->createQueryForType($this->objectType);
                if ($this->defaultOrderings !== array()) {
                        $query->setOrderings($this->defaultOrderings);
                }
index 96a1fd2..06aa850 100644 (file)
@@ -53,16 +53,6 @@ interface RepositoryInterface {
        public function remove($object);
 
        /**
-        * Replaces an object by another.
-        *
-        * @param object $existingObject The existing object
-        * @param object $newObject The new object
-        * @return void
-        * @api
-        */
-       public function replace($existingObject, $newObject);
-
-       /**
         * Replaces an existing object with the same identifier by the given object
         *
         * @param object $modifiedObject The modified object
@@ -71,22 +61,6 @@ interface RepositoryInterface {
        public function update($modifiedObject);
 
        /**
-        * Returns all objects of this repository add()ed but not yet persisted to
-        * the storage layer.
-        *
-        * @return array An array of objects
-        */
-       public function getAddedObjects();
-
-       /**
-        * Returns an array with objects remove()d from the repository that
-        * had been persisted to the storage layer before.
-        *
-        * @return array
-        */
-       public function getRemovedObjects();
-
-       /**
         * Returns all objects of this repository.
         *
         * @return \TYPO3\CMS\Extbase\Persistence\QueryResultInterface|array The query result
@@ -121,6 +95,15 @@ interface RepositoryInterface {
        public function findByUid($uid);
 
        /**
+        * Finds an object matching the given identifier.
+        *
+        * @param mixed $identifier The identifier of the object to find
+        * @return object The matching object if found, otherwise NULL
+        * @api
+        */
+       public function findByIdentifier($identifier);
+
+       /**
         * Sets the property names to order the result by per default.
         * Expected like this:
         * array(
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/Entity2.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/Entity2.php
new file mode 100644 (file)
index 0000000..9b6542b
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+namespace TYPO3\CMS\Extbase\Tests\Persistence\Fixture\Model;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  This class is a backport of the corresponding class of TYPO3 Flow.
+ *  All credits go to the TYPO3 Flow team.
+ *  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.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  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!
+ ***************************************************************/
+
+/**
+ * A model fixture used for testing the persistence manager
+ *
+ */
+class Entity2 extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
+
+       /**
+        * Just a normal string
+        *
+        * @var string
+        */
+       public $someString;
+
+       /**
+        * @var integer
+        */
+       public $someInteger;
+
+       /**
+        * @var \TYPO3\CMS\Extbase\Tests\Persistence\Fixture\Model\Entity3
+        */
+       public $someReference;
+
+       /**
+        * @var array
+        */
+       public $someReferenceArray = array();
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/Entity3.php b/typo3/sysext/extbase/Tests/Unit/Persistence/Fixture/Model/Entity3.php
new file mode 100644 (file)
index 0000000..6b2d64b
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+namespace TYPO3\CMS\Extbase\Tests\Persistence\Fixture\Model;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A model fixture used for testing the persistence manager
+ *
+ */
+class Entity3 extends \TYPO3\CMS\Extbase\DomainObject\AbstractEntity {
+
+       /**
+        * Just a normal string
+        *
+        * @var string
+        */
+       public $someString;
+
+       /**
+        * @var integer
+        */
+       public $someInteger;
+
+}
+?>
\ No newline at end of file
index 2b3630b..b55e1e7 100644 (file)
@@ -26,6 +26,10 @@ namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic;
  *
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
+
+require_once __DIR__ . '/../Fixture/Model/Entity3.php';
+require_once __DIR__ . '/../Fixture/Model/Entity2.php';
+
 /**
  * A PersistenceManager Test
  */
@@ -33,6 +37,271 @@ class PersistenceManagerTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase
 
        /**
         * @test
+        */
+       public function persistAllPassesAddedObjectsToBackend() {
+               $entity2 = new \TYPO3\CMS\Extbase\Tests\Persistence\Fixture\Model\Entity2();
+               $objectStorage = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $objectStorage->attach($entity2);
+               $mockBackend = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface');
+               $mockBackend->expects($this->once())->method('setAggregateRootObjects')->with($objectStorage);
+
+               $manager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $manager->injectBackend($mockBackend);
+               $manager->add($entity2);
+
+               $manager->persistAll();
+       }
+
+       /**
+        * @test
+        */
+       public function persistAllPassesRemovedObjectsToBackend() {
+               $entity2 = new \TYPO3\CMS\Extbase\Tests\Persistence\Fixture\Model\Entity2();
+               $objectStorage = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $objectStorage->attach($entity2);
+               $mockBackend = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface');
+               $mockBackend->expects($this->once())->method('setDeletedEntities')->with($objectStorage);
+
+               $manager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $manager->injectBackend($mockBackend);
+               $manager->remove($entity2);
+
+               $manager->persistAll();
+       }
+
+       /**
+        * @test
+        */
+       public function getIdentifierByObjectReturnsIdentifierFromSession() {
+               $fakeUuid = 'fakeUuid';
+               $object = new \stdClass();
+
+               $mockSession = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\Session');
+               $mockSession->expects($this->once())->method('getIdentifierByObject')->with($object)->will($this->returnValue($fakeUuid));
+
+               $manager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $manager->injectPersistenceSession($mockSession);
+
+               $this->assertEquals($manager->getIdentifierByObject($object), $fakeUuid);
+       }
+
+       /**
+        * @test
+        */
+       public function getObjectByIdentifierReturnsObjectFromSessionIfAvailable() {
+               $fakeUuid = 'fakeUuid';
+               $object = new \stdClass();
+
+               $mockSession = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\Session');
+               $mockSession->expects($this->once())->method('hasIdentifier')->with($fakeUuid)->will($this->returnValue(TRUE));
+               $mockSession->expects($this->once())->method('getObjectByIdentifier')->with($fakeUuid)->will($this->returnValue($object));
+
+               $manager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $manager->injectPersistenceSession($mockSession);
+
+               $this->assertEquals($manager->getObjectByIdentifier($fakeUuid), $object);
+       }
+
+       /**
+        * @test
+        */
+       public function getObjectByIdentifierReturnsObjectFromPersistenceIfAvailable() {
+               $fakeUuid = '42';
+               $object = new \stdClass();
+               $fakeEntityType = get_class($object);
+
+               $mockSession = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\Session');
+               $mockSession->expects($this->once())->method('hasIdentifier')->with($fakeUuid)->will($this->returnValue(FALSE));
+
+               $mockBackend = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface');
+               $mockBackend->expects($this->once())->method('getObjectByIdentifier')->with($fakeUuid, $fakeEntityType)->will($this->returnValue($object));
+
+               $manager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $manager->injectPersistenceSession($mockSession);
+               $manager->injectBackend($mockBackend);
+
+               $this->assertEquals($manager->getObjectByIdentifier($fakeUuid, $fakeEntityType), $object);
+       }
+
+       /**
+        * @test
+        */
+       public function getObjectByIdentifierReturnsNullForUnknownObject() {
+               $fakeUuid = '42';
+               $fakeEntityType = 'foobar';
+
+               $mockSession = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\Session');
+               $mockSession->expects($this->once())->method('hasIdentifier')->with($fakeUuid, $fakeEntityType)->will($this->returnValue(FALSE));
+
+               $mockBackend = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface');
+               $mockBackend->expects($this->once())->method('getObjectByIdentifier')->with($fakeUuid, $fakeEntityType)->will($this->returnValue(NULL));
+
+               $manager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $manager->injectPersistenceSession($mockSession);
+               $manager->injectBackend($mockBackend);
+
+               $this->assertNull($manager->getObjectByIdentifier($fakeUuid, $fakeEntityType));
+       }
+
+       /**
+        * @test
+        */
+       public function addActuallyAddsAnObjectToTheInternalObjectsArray() {
+               $someObject = new \stdClass();
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $persistenceManager->add($someObject);
+
+               $this->assertAttributeContains($someObject, 'addedObjects', $persistenceManager);
+       }
+
+       /**
+        * @test
+        */
+       public function removeActuallyRemovesAnObjectFromTheInternalObjectsArray() {
+               $object1 = new \stdClass();
+               $object2 = new \stdClass();
+               $object3 = new \stdClass();
+
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $persistenceManager->add($object1);
+               $persistenceManager->add($object2);
+               $persistenceManager->add($object3);
+
+               $persistenceManager->remove($object2);
+
+               $this->assertAttributeContains($object1, 'addedObjects', $persistenceManager);
+               $this->assertAttributeNotContains($object2, 'addedObjects', $persistenceManager);
+               $this->assertAttributeContains($object3, 'addedObjects', $persistenceManager);
+       }
+
+       /**
+        * @test
+        */
+       public function removeRemovesTheRightObjectEvenIfItHasBeenModifiedSinceItsAddition() {
+               $object1 = new \ArrayObject(array('val' => '1'));
+               $object2 = new \ArrayObject(array('val' => '2'));
+               $object3 = new \ArrayObject(array('val' => '3'));
+
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $persistenceManager->add($object1);
+               $persistenceManager->add($object2);
+               $persistenceManager->add($object3);
+
+               $object2['foo'] = 'bar';
+               $object3['val'] = '2';
+
+               $persistenceManager->remove($object2);
+
+               $this->assertAttributeContains($object1, 'addedObjects', $persistenceManager);
+               $this->assertAttributeNotContains($object2, 'addedObjects', $persistenceManager);
+               $this->assertAttributeContains($object3, 'addedObjects', $persistenceManager);
+       }
+
+       /**
+        * Make sure we remember the objects that are not currently add()ed
+        * but might be in persistent storage.
+        *
+        * @test
+        */
+       public function removeRetainsObjectForObjectsNotInCurrentSession() {
+               $object = new \ArrayObject(array('val' => '1'));
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $persistenceManager->remove($object);
+
+               $this->assertAttributeContains($object, 'removedObjects', $persistenceManager);
+       }
+
+       /**
+        * @test
+        */
+       public function updateSchedulesAnObjectForPersistence() {
+               $className = uniqid('BazFixture');
+               eval ('
+                       namespace Foo\\Bar\\Domain\\Model;
+                       class ' . $className . ' extends \\TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity {
+                               protected $uid = 42;
+                       }
+               ');
+               eval ('
+                       namespace Foo\\Bar\\Domain\\Repository;
+                       class  ' . $className . 'Repository extends \\TYPO3\\CMS\\Extbase\\Persistence\\Repository {}
+               ');
+               $classNameWithNamespace = 'Foo\\Bar\\Domain\\Model\\' . $className;
+               $repositorClassNameWithNamespace = 'Foo\\Bar\\Domain\\Repository\\' . $className . 'Repository';
+
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $changedEntities = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
+               $entity1 = new $classNameWithNamespace();
+               $repository = new $repositorClassNameWithNamespace;
+               $mockBackend = $this->getMock($this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Backend'), array('commit', 'setChangedEntities'), array(), '', FALSE);
+               $mockBackend->expects($this->once())
+                       ->method('setChangedEntities')
+                       ->with($this->equalTo($changedEntities));
+
+               $persistenceManager->injectBackend($mockBackend);
+               $persistenceManager->injectPersistenceSession($session);
+               $repository->injectPersistenceManager($persistenceManager);
+
+               $session->registerObject($entity1, 42);
+               $changedEntities->attach($entity1);
+               $repository->update($entity1);
+               $persistenceManager->persistAll();
+
+       }
+
+       /**
+        * @test
+        */
+       public function clearStateForgetsAboutNewObjects() {
+               $mockObject = $this->getMock('TYPO3\CMS\Extbase\Persistence\Aspect\PersistenceMagicInterface');
+               $mockObject->Persistence_Object_Identifier = 'abcdefg';
+
+               $mockSession = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\Session');
+               $mockSession->expects($this->any())->method('hasIdentifier')->will($this->returnValue(FALSE));
+               $mockBackend = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface');
+               $mockBackend->expects($this->any())->method('getObjectDataByIdentifier')->will($this->returnValue(FALSE));
+
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $persistenceManager->injectPersistenceSession($mockSession);
+               $persistenceManager->injectBackend($mockBackend);
+
+               $persistenceManager->registerNewObject($mockObject);
+               $persistenceManager->clearState();
+
+               $object = $persistenceManager->getObjectByIdentifier('abcdefg');
+               $this->assertNull($object);
+       }
+
+       /**
+        * @test
+        */
+       public function tearDownWithBackendNotSupportingTearDownDoesNothing() {
+               $mockBackend = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface');
+               $mockBackend->expects($this->never())->method('tearDown');
+
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $persistenceManager->injectBackend($mockBackend);
+
+               $persistenceManager->tearDown();
+       }
+
+       /**
+        * @test
+        */
+       public function tearDownWithBackendSupportingTearDownDelegatesCallToBackend() {
+               $methods = array_merge(get_class_methods('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface'), array('tearDown'));
+               $mockBackend = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface', $methods);
+               $mockBackend->expects($this->once())->method('tearDown');
+
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
+               $persistenceManager->injectBackend($mockBackend);
+
+               $persistenceManager->tearDown();
+       }
+
+       /**
+        * @test
         *
         * This test and the related Fixtures TxDomainModelTestEntity and
         * TxDomainRepositoryTestEntityRepository can be removed if we do not need to support
@@ -46,20 +315,17 @@ class PersistenceManagerTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase
                eval ('
                        class Foo_Bar_Domain_Repository_' . $className . 'Repository {}
                ');
-
-               $persistenceSession = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
                $aggregateRootObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
                $fullClassName = 'Foo_Bar_Domain_Model_' . $className;
                $entity1 = new $fullClassName();
                $aggregateRootObjects->attach($entity1);
-               $persistenceSession->registerReconstitutedObject($entity1);
-               $mockTypo3DbBackend = $this->getMock($this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Storage\\Typo3DbBackend'), array('commit', 'setAggregateRootObjects', 'setDeletedObjects'), array(), '', FALSE);
-               $persistenceManager = $this->getMock($this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\PersistenceManager'), array('dummy'), array(), '', FALSE);
-               $mockTypo3DbBackend->expects($this->once())
+               $mockBackend = $this->getMock($this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Backend'), array('commit', 'setAggregateRootObjects', 'setDeletedEntities'), array(), '', FALSE);
+               $mockBackend->expects($this->once())
                        ->method('setAggregateRootObjects')
                        ->with($this->equalTo($aggregateRootObjects));
-               $persistenceManager->_set('backend', $mockTypo3DbBackend);
-               $persistenceManager->injectSession($persistenceSession);
+               $persistenceManager->injectBackend($mockBackend);
+               $persistenceManager->add($entity1);
                $persistenceManager->persistAll();
        }
 
@@ -76,22 +342,19 @@ class PersistenceManagerTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase
                        namespace Foo\\Bar\\Domain\\Repository;
                        class  ' . $className . 'Repository {}
                ');
-
-               $persistenceSession = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $persistenceManager = new \TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager();
                $aggregateRootObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
                $classNameWithNamespace = 'Foo\\Bar\\Domain\\Model\\' . $className;
                $entity1 = new $classNameWithNamespace();
                $aggregateRootObjects->attach($entity1);
-               $persistenceSession->registerReconstitutedObject($entity1);
-               $mockTypo3DbBackend = $this->getMock($this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Storage\\Typo3DbBackend'), array('commit', 'setAggregateRootObjects', 'setDeletedObjects'), array(), '', FALSE);
-               $persistenceManager = $this->getMock($this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\PersistenceManager'), array('dummy'), array(), '', FALSE);
-               $mockTypo3DbBackend->expects($this->once())
+               $mockBackend = $this->getMock($this->buildAccessibleProxy('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Backend'), array('commit', 'setAggregateRootObjects', 'setDeletedEntities'), array(), '', FALSE);
+               $mockBackend->expects($this->once())
                        ->method('setAggregateRootObjects')
                        ->with($this->equalTo($aggregateRootObjects));
-               $persistenceManager->_set('backend', $mockTypo3DbBackend);
-               $persistenceManager->injectSession($persistenceSession);
+               $persistenceManager->injectBackend($mockBackend);
+               $persistenceManager->add($entity1);
                $persistenceManager->persistAll();
        }
-}
 
+}
 ?>
\ No newline at end of file
index bf329c1..426bbe6 100644 (file)
@@ -32,9 +32,104 @@ class SessionTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
+       public function objectRegisteredWithRegisterReconstitutedEntityCanBeRetrievedWithGetReconstitutedEntities() {
+               $someObject = new \ArrayObject(array());
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $session->registerReconstitutedEntity($someObject, array('identifier' => 'fakeUuid'));
+
+               $ReconstitutedEntities = $session->getReconstitutedEntities();
+               $this->assertTrue($ReconstitutedEntities->contains($someObject));
+       }
+
+       /**
+        * @test
+        */
+       public function unregisterReconstitutedEntityRemovesObjectFromSession() {
+               $someObject = new \ArrayObject(array());
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $session->registerObject($someObject, 'fakeUuid');
+               $session->registerReconstitutedEntity($someObject, array('identifier' => 'fakeUuid'));
+               $session->unregisterReconstitutedEntity($someObject);
+
+               $ReconstitutedEntities = $session->getReconstitutedEntities();
+               $this->assertFalse($ReconstitutedEntities->contains($someObject));
+       }
+
+       /**
+        * @test
+        */
+       public function hasObjectReturnsTrueForRegisteredObject() {
+               $object1 = new \stdClass();
+               $object2 = new \stdClass();
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $session->registerObject($object1, 12345);
+
+               $this->assertTrue($session->hasObject($object1), 'Session claims it does not have registered object.');
+               $this->assertFalse($session->hasObject($object2), 'Session claims it does have unregistered object.');
+       }
+
+       /**
+        * @test
+        */
+       public function hasIdentifierReturnsTrueForRegisteredObject() {
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $session->registerObject(new \stdClass(), 12345);
+
+               $this->assertTrue($session->hasIdentifier('12345', 'stdClass'), 'Session claims it does not have registered object.');
+               $this->assertFalse($session->hasIdentifier('67890', 'stdClass'), 'Session claims it does have unregistered object.');
+       }
+
+       /**
+        * @test
+        */
+       public function getIdentifierByObjectReturnsRegisteredUUIDForObject() {
+               $object = new \stdClass();
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $session->registerObject($object, 12345);
+
+               $this->assertEquals($session->getIdentifierByObject($object), 12345, 'Did not get UUID registered for object.');
+       }
+
+       /**
+        * @test
+        */
+       public function getObjectByIdentifierReturnsRegisteredObjectForUUID() {
+               $object = new \stdClass();
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $session->registerObject($object, 12345);
+
+               $this->assertSame($session->getObjectByIdentifier('12345', 'stdClass'), $object, 'Did not get object registered for UUID.');
+       }
+
+       /**
+        * @test
+        */
+       public function unregisterObjectRemovesRegisteredObject() {
+               $object1 = new \stdClass();
+               $object2 = new \stdClass();
+               $session = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
+               $session->registerObject($object1, 12345);
+               $session->registerObject($object2, 67890);
+
+               $this->assertTrue($session->hasObject($object1), 'Session claims it does not have registered object.');
+               $this->assertTrue($session->hasIdentifier('12345', 'stdClass'), 'Session claims it does not have registered object.');
+               $this->assertTrue($session->hasObject($object1), 'Session claims it does not have registered object.');
+               $this->assertTrue($session->hasIdentifier('67890', 'stdClass'), 'Session claims it does not have registered object.');
+
+               $session->unregisterObject($object1);
+
+               $this->assertFalse($session->hasObject($object1), 'Session claims it does have unregistered object.');
+               $this->assertFalse($session->hasIdentifier('12345', 'stdClass'), 'Session claims it does not have registered object.');
+               $this->assertTrue($session->hasObject($object2), 'Session claims it does not have registered object.');
+               $this->assertTrue($session->hasIdentifier('67890', 'stdClass'), 'Session claims it does not have registered object.');
+       }
+
+       /**
+        * @test
+        */
        public function newSessionIsEmpty() {
                $persistenceSession = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
-               $reconstitutedObjects = $persistenceSession->getReconstitutedObjects();
+               $reconstitutedObjects = $persistenceSession->getReconstitutedEntities();
                $this->assertEquals(0, count($reconstitutedObjects), 'The reconstituted objects storage was not empty.');
        }
 
@@ -44,8 +139,8 @@ class SessionTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        public function objectCanBeRegisteredAsReconstituted() {
                $persistenceSession = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
                $entity = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity');
-               $persistenceSession->registerReconstitutedObject($entity);
-               $reconstitutedObjects = $persistenceSession->getReconstitutedObjects();
+               $persistenceSession->registerReconstitutedEntity($entity);
+               $reconstitutedObjects = $persistenceSession->getReconstitutedEntities();
                $this->assertTrue($reconstitutedObjects->contains($entity), 'The object was not registered as reconstituted.');
        }
 
@@ -55,9 +150,9 @@ class SessionTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        public function objectCanBeUnregisteredAsReconstituted() {
                $persistenceSession = new \TYPO3\CMS\Extbase\Persistence\Generic\Session();
                $entity = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractEntity');
-               $persistenceSession->registerReconstitutedObject($entity);
-               $persistenceSession->unregisterReconstitutedObject($entity);
-               $reconstitutedObjects = $persistenceSession->getReconstitutedObjects();
+               $persistenceSession->registerReconstitutedEntity($entity);
+               $persistenceSession->unregisterReconstitutedEntity($entity);
+               $reconstitutedObjects = $persistenceSession->getReconstitutedEntities();
                $this->assertEquals(0, count($reconstitutedObjects), 'The reconstituted objects storage was not empty.');
        }
 }
index be0626d..6eddaef 100644 (file)
@@ -85,13 +85,10 @@ class RepositoryTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                $this->mockBackend = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\BackendInterface');
                $this->mockSession = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Session');
                $this->mockPersistenceManager = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\PersistenceManagerInterface');
+               $this->mockPersistenceManager->expects($this->any())->method('createQueryForType')->will($this->returnValue($this->mockQuery));
                $this->mockObjectManager = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManagerInterface');
                $this->repository = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Repository', array('dummy'), array($this->mockObjectManager));
-               $this->repository->injectIdentityMap($this->mockIdentityMap);
-               $this->repository->injectQueryFactory($this->mockQueryFactory);
                $this->repository->injectPersistenceManager($this->mockPersistenceManager);
-               $this->repository->injectBackend($this->mockBackend);
-               $this->repository->injectSession($this->mockSession);
        }
 
        /**
@@ -104,276 +101,269 @@ class RepositoryTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
        /**
         * @test
         */
-       public function addActuallyAddsAnObjectToTheInternalObjectsArray() {
-               $someObject = new \stdClass();
-               $this->repository->_set('objectType', get_class($someObject));
-               $this->repository->add($someObject);
-               $this->assertTrue($this->repository->getAddedObjects()->contains($someObject));
+       public function createQueryCallsPersistenceManagerWithExpectedClassName() {
+               $mockPersistenceManager = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager');
+               $mockPersistenceManager->expects($this->once())->method('createQueryForType')->with('ExpectedType');
+
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $repository->_set('objectType', 'ExpectedType');
+               $this->inject($repository, 'persistenceManager', $mockPersistenceManager);
+
+               $repository->createQuery();
        }
 
        /**
         * @test
         */
-       public function removeActuallyRemovesAnObjectFromTheInternalObjectsArray() {
-               $object1 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
-               $object2 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
-               $object3 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
-               $this->repository->_set('objectType', get_class($object1));
-               $this->repository->add($object1);
-               $this->repository->add($object2);
-               $this->repository->add($object3);
-               $this->repository->remove($object2);
-               $this->assertTrue($this->repository->getAddedObjects()->contains($object1));
-               $this->assertFalse($this->repository->getAddedObjects()->contains($object2));
-               $this->assertTrue($this->repository->getAddedObjects()->contains($object3));
+       public function createQuerySetsDefaultOrderingIfDefined() {
+               $orderings = array('foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING);
+               $mockQuery = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryInterface');
+               $mockQuery->expects($this->once())->method('setOrderings')->with($orderings);
+               $mockPersistenceManager = $this->getMock('TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager');
+               $mockPersistenceManager->expects($this->exactly(2))->method('createQueryForType')->with('ExpectedType')->will($this->returnValue($mockQuery));
+
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $repository->_set('objectType', 'ExpectedType');
+               $this->inject($repository, 'persistenceManager', $mockPersistenceManager);
+               $repository->setDefaultOrderings($orderings);
+               $repository->createQuery();
+
+               $repository->setDefaultOrderings(array());
+               $repository->createQuery();
        }
 
        /**
         * @test
         */
-       public function removeRemovesTheRightObjectEvenIfItHasBeenModifiedSinceItsAddition() {
-               $object1 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
-               $object2 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
-               $object3 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
-               $this->repository->_set('objectType', get_class($object1));
-               $this->repository->add($object1);
-               $this->repository->add($object2);
-               $this->repository->add($object3);
-               $object2->setPid(1);
-               $object3->setPid(2);
-               $this->repository->remove($object2);
-               $this->assertTrue($this->repository->getAddedObjects()->contains($object1));
-               $this->assertFalse($this->repository->getAddedObjects()->contains($object2));
-               $this->assertTrue($this->repository->getAddedObjects()->contains($object3));
+       public function findAllCreatesQueryAndReturnsResultOfExecuteCall() {
+               $expectedResult = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryResultInterface');
+
+               $mockQuery = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryInterface');
+               $mockQuery->expects($this->once())->method('execute')->with()->will($this->returnValue($expectedResult));
+
+               $repository = $this->getMock('TYPO3\CMS\Extbase\Persistence\Repository', array('createQuery'));
+               $repository->expects($this->once())->method('createQuery')->will($this->returnValue($mockQuery));
+
+               $this->assertSame($expectedResult, $repository->findAll());
        }
 
        /**
-        * Make sure we remember the objects that are not currently add()ed
-        * but might be in persistent storage.
-        *
         * @test
         */
-       public function removeRetainsObjectForObjectsNotInCurrentSession() {
-               $object = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
-               // if the object is not currently add()ed, it is not new
-               $object->expects($this->once())->method('_isNew')->will($this->returnValue(FALSE));
-               $this->repository->_set('objectType', get_class($object));
-               $this->repository->remove($object);
-               $this->assertTrue($this->repository->getRemovedObjects()->contains($object));
+       public function findByidentifierReturnsResultOfGetObjectByIdentifierCall() {
+               $identifier = '42';
+               $object = new \stdClass();
+
+               $mockPersistenceManager = $this->getMock('TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface');
+               $mockPersistenceManager->expects($this->once())->method('getObjectByIdentifier')->with($identifier, 'stdClass')->will($this->returnValue($object));
+
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('createQuery'));
+               $this->inject($repository, 'persistenceManager', $mockPersistenceManager);
+               $repository->_set('objectType', 'stdClass');
+
+               $this->assertSame($object, $repository->findByIdentifier($identifier));
        }
 
        /**
-        * dataProvider for createQueryCallsQueryFactoryWithExpectedType
-        *
-        * @return array
+        * @test
         */
-       public function modelAndRepositoryClassNames() {
-               return array(
-                       array('Tx_BlogExample_Domain_Repository_BlogRepository', 'Tx_BlogExample_Domain_Model_Blog'),
-                       array('_Domain_Repository_Content_PageRepository', '_Domain_Model_Content_Page'),
-                       array('Tx_RepositoryExample_Domain_Repository_SomeModelRepository', 'Tx_RepositoryExample_Domain_Model_SomeModel'),
-                       array('Tx_RepositoryExample_Domain_Repository_RepositoryRepository', 'Tx_RepositoryExample_Domain_Model_Repository'),
-                       array('Tx_Repository_Domain_Repository_RepositoryRepository', 'Tx_Repository_Domain_Model_Repository')
-               );
+       public function addDelegatesToPersistenceManager() {
+               $object = new \stdClass();
+               $mockPersistenceManager = $this->getMock('TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface');
+               $mockPersistenceManager->expects($this->once())->method('add')->with($object);
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $this->inject($repository, 'persistenceManager', $mockPersistenceManager);
+               $repository->_set('objectType', get_class($object));
+               $repository->add($object);
        }
 
        /**
         * @test
-        * @dataProvider modelAndRepositoryClassNames
-        * @param string $repositoryClassName
-        * @param string $modelClassName
         */
-       public function constructSetsObjectTypeFromClassName($repositoryClassName, $modelClassName) {
-               $mockClassName = 'MockRepository' . uniqid();
-               eval('class ' . $mockClassName . ' extends TYPO3\\CMS\\Extbase\\Persistence\\Repository {
-                       protected function getRepositoryClassName() {
-                               return \'' . $repositoryClassName . '\';
-                       }
-                       public function _getObjectType() {
-                               return $this->objectType;
-                       }
-               }');
-               $this->repository = new $mockClassName($this->mockObjectManager);
-               $this->assertEquals($modelClassName, $this->repository->_getObjectType());
+       public function removeDelegatesToPersistenceManager() {
+               $object = new \stdClass();
+               $mockPersistenceManager = $this->getMock('TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface');
+               $mockPersistenceManager->expects($this->once())->method('remove')->with($object);
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $this->inject($repository, 'persistenceManager', $mockPersistenceManager);
+               $repository->_set('objectType', get_class($object));
+               $repository->remove($object);
        }
 
        /**
         * @test
         */
-       public function createQueryCallsQueryFactoryWithExpectedClassName() {
-               $this->mockQueryFactory->expects($this->once())->method('create')->with('ExpectedType');
-               $this->repository->_set('objectType', 'ExpectedType');
-               $this->repository->createQuery();
+       public function updateDelegatesToPersistenceManager() {
+               $object = new \stdClass();
+               $mockPersistenceManager = $this->getMock('TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface');
+               $mockPersistenceManager->expects($this->once())->method('update')->with($object);
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $this->inject($repository, 'persistenceManager', $mockPersistenceManager);
+               $repository->_set('objectType', get_class($object));
+               $repository->update($object);
        }
 
        /**
         * @test
         */
-       public function createQueryReturnsQueryWithUnmodifiedDefaultQuerySettings() {
-               $mockQueryFactory = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QueryFactory');
-               $mockQuery = new \TYPO3\CMS\Extbase\Persistence\Generic\Query('foo');
-               $mockDefaultQuerySettings = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
-               $this->repository->injectQueryFactory($mockQueryFactory);
-               $this->repository->setDefaultQuerySettings($mockDefaultQuerySettings);
-               $mockQueryFactory->expects($this->once())->method('create')->will($this->returnValue($mockQuery));
-               $this->repository->createQuery();
-               $instanceQuerySettings = $mockQuery->getQuerySettings();
-               $this->assertEquals($mockDefaultQuerySettings, $instanceQuerySettings);
-               $this->assertNotSame($mockDefaultQuerySettings, $instanceQuerySettings);
+       public function magicCallMethodAcceptsFindBySomethingCallsAndExecutesAQueryWithThatCriteria() {
+               $mockQueryResult = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryResultInterface');
+               $mockQuery = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryInterface');
+               $mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
+               $mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($mockQuery));
+               $mockQuery->expects($this->once())->method('execute')->with()->will($this->returnValue($mockQueryResult));
+
+               $repository = $this->getMock('TYPO3\CMS\Extbase\Persistence\Repository', array('createQuery'));
+               $repository->expects($this->once())->method('createQuery')->will($this->returnValue($mockQuery));
+
+               $this->assertSame($mockQueryResult, $repository->findByFoo('bar'));
        }
 
        /**
         * @test
         */
-       public function findAllCreatesQueryAndReturnsResultOfExecuteCall() {
-               $expectedResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
-               $this->mockQuery->expects($this->once())->method('execute')->with()->will($this->returnValue($expectedResult));
-               $this->assertSame($expectedResult, $this->repository->findAll());
+       public function magicCallMethodAcceptsFindOneBySomethingCallsAndExecutesAQueryWithThatCriteria() {
+               $object = new \stdClass();
+               $mockQueryResult = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryResultInterface');
+               $mockQueryResult->expects($this->once())->method('getFirst')->will($this->returnValue($object));
+               $mockQuery = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryInterface');
+               $mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
+               $mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($mockQuery));
+               $mockQuery->expects($this->once())->method('setLimit')->will($this->returnValue($mockQuery));
+               $mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
+
+               $repository = $this->getMock('TYPO3\CMS\Extbase\Persistence\Repository', array('createQuery'));
+               $repository->expects($this->once())->method('createQuery')->will($this->returnValue($mockQuery));
+
+               $this->assertSame($object, $repository->findOneByFoo('bar'));
        }
 
        /**
         * @test
         */
-       public function findByUidReturnsResultOfGetObjectByIdentifierCall() {
-               $fakeUid = '123';
-               $object = new \stdClass();
-               $this->repository->_set('objectType', 'someObjectType');
-               $this->mockIdentityMap->expects($this->once())->method('hasIdentifier')->with($fakeUid, 'someObjectType')->will($this->returnValue(TRUE));
-               $this->mockIdentityMap->expects($this->once())->method('getObjectByIdentifier')->with($fakeUid)->will($this->returnValue($object));
-               $expectedResult = $object;
-               $actualResult = $this->repository->findByUid($fakeUid);
-               $this->assertSame($expectedResult, $actualResult);
+       public function magicCallMethodAcceptsCountBySomethingCallsAndExecutesAQueryWithThatCriteria() {
+               $mockQuery = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryInterface');
+               $mockQueryResult = $this->getMock('TYPO3\CMS\Extbase\Persistence\QueryResultInterface');
+               $mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
+               $mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($mockQuery));
+               $mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
+               $mockQueryResult->expects($this->once())->method('count')->will($this->returnValue(2));
+
+               $repository = $this->getMock('TYPO3\CMS\Extbase\Persistence\Repository', array('createQuery'));
+               $repository->expects($this->once())->method('createQuery')->will($this->returnValue($mockQuery));
+
+               $this->assertSame(2, $repository->countByFoo('bar'));
        }
 
        /**
-        * Replacing a reconstituted object (which has a uuid) by a new object
-        * will ask the persistence backend to replace them accordingly in the
-        * identity map.
-        *
         * @test
-        * @return void
-        */
-       public function replaceReplacesReconstitutedEntityByNewObject() {
-               $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $this->mockPersistenceManager->expects($this->once())->method('getIdentifierByObject')->with($existingObject)->will($this->returnValue('123'));
-               $this->mockBackend->expects($this->once())->method('replaceObject')->with($existingObject, $newObject);
-               $this->repository->_set('objectType', get_class($newObject));
-               $this->repository->replace($existingObject, $newObject);
+        * @expectedException \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedMethodException
+        */
+       public function magicCallMethodTriggersAnErrorIfUnknownMethodsAreCalled() {
+               $repository = $this->getMock('TYPO3\CMS\Extbase\Persistence\Repository', array('createQuery'));
+               $repository->__call('foo', array());
        }
 
        /**
-        * Replacing a reconstituted object which during this session has been
-        * marked for removal (by calling the repository's remove method)
-        * additionally registers the "newObject" for removal and removes the
-        * "existingObject" from the list of removed objects.
-        *
         * @test
-        * @return void
-        */
-       public function replaceRemovesReconstitutedObjectWhichIsMarkedToBeRemoved() {
-               $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $removedObjects = new \SplObjectStorage();
-               $removedObjects->attach($existingObject);
-               $this->mockPersistenceManager->expects($this->once())->method('getIdentifierByObject')->with($existingObject)->will($this->returnValue('123'));
-               $this->mockBackend->expects($this->once())->method('replaceObject')->with($existingObject, $newObject);
-               $this->repository->_set('objectType', get_class($newObject));
-               $this->repository->_set('removedObjects', $removedObjects);
-               $this->repository->replace($existingObject, $newObject);
-               $this->assertFalse($removedObjects->contains($existingObject));
-               $this->assertTrue($removedObjects->contains($newObject));
+        * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
+        */
+       public function addChecksObjectType() {
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $repository->_set('objectType', 'ExpectedObjectType');
+
+               $repository->add(new \stdClass());
        }
 
        /**
-        * Replacing a new object which has not yet been persisted by another
-        * new object will just replace them in the repository's list of added
-        * objects.
-        *
         * @test
-        * @return void
-        */
-       public function replaceAddsNewObjectToAddedObjects() {
-               $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $addedObjects = new \SplObjectStorage();
-               $addedObjects->attach($existingObject);
-               $this->mockPersistenceManager->expects($this->once())->method('getIdentifierByObject')->with($existingObject)->will($this->returnValue(NULL));
-               $this->mockBackend->expects($this->never())->method('replaceObject');
-               $this->repository->_set('objectType', get_class($newObject));
-               $this->repository->_set('addedObjects', $addedObjects);
-               $this->repository->replace($existingObject, $newObject);
-               $this->assertFalse($addedObjects->contains($existingObject));
-               $this->assertTrue($addedObjects->contains($newObject));
-       }
+        * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
+        */
+       public function removeChecksObjectType() {
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $repository->_set('objectType', 'ExpectedObjectType');
 
+               $repository->remove(new \stdClass());
+       }
        /**
         * @test
         * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
         */
-       public function replaceChecksObjectType() {
-               $this->repository->_set('objectType', 'ExpectedObjectType');
-               $this->repository->replace(new \stdClass(), new \stdClass());
+       public function updateChecksObjectType() {
+               $repository = $this->getAccessibleMock('TYPO3\CMS\Extbase\Persistence\Repository', array('dummy'));
+               $repository->_set('objectType', 'ExpectedObjectType');
+
+               $repository->update(new \stdClass());
        }
 
        /**
-        * @test
+        * dataProvider for createQueryCallsQueryFactoryWithExpectedType
+        *
+        * @return array
         */
-       public function updateReplacesAnObjectWithTheSameUuidByTheGivenObject() {
-               $existingObject = new \stdClass();
-               $modifiedObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $modifiedObject->expects($this->once())->method('getUid')->will($this->returnValue('123'));
-               /** @var \TYPO3\CMS\Extbase\Persistence\Repository|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
-               $repository = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Repository', array('findByUid', 'replace'), array($this->mockObjectManager));
-               $repository->expects($this->once())->method('findByUid')->with('123')->will($this->returnValue($existingObject));
-               $repository->expects($this->once())->method('replace')->with($existingObject, $modifiedObject);
-               $repository->_set('objectType', get_class($modifiedObject));
-               $repository->update($modifiedObject);
+       public function modelAndRepositoryClassNames() {
+               return array(
+                       array('Tx_BlogExample_Domain_Repository_BlogRepository', 'Tx_BlogExample_Domain_Model_Blog'),
+                       array('_Domain_Repository_Content_PageRepository', '_Domain_Model_Content_Page'),
+                       array('Tx_RepositoryExample_Domain_Repository_SomeModelRepository', 'Tx_RepositoryExample_Domain_Model_SomeModel'),
+                       array('Tx_RepositoryExample_Domain_Repository_RepositoryRepository', 'Tx_RepositoryExample_Domain_Model_Repository'),
+                       array('Tx_Repository_Domain_Repository_RepositoryRepository', 'Tx_Repository_Domain_Model_Repository')
+               );
        }
 
        /**
         * @test
-        * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
+        * @dataProvider modelAndRepositoryClassNames
+        * @param string $repositoryClassName
+        * @param string $modelClassName
         */
-       public function updateRejectsUnknownObjects() {
-               $someObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
-               $someObject->expects($this->once())->method('getUid')->will($this->returnValue(NULL));
-               $this->repository->_set('objectType', get_class($someObject));
-               $this->repository->update($someObject);
+       public function constructSetsObjectTypeFromClassName($repositoryClassName, $modelClassName) {
+               $mockClassName = 'MockRepository' . uniqid();
+               eval('class ' . $mockClassName . ' extends TYPO3\\CMS\\Extbase\\Persistence\\Repository {
+                       protected function getRepositoryClassName() {
+                               return \'' . $repositoryClassName . '\';
+                       }
+                       public function _getObjectType() {
+                               return $this->objectType;
+                       }
+               }');
+               $this->repository = new $mockClassName($this->mockObjectManager);
+               $this->assertEquals($modelClassName, $this->repository->_getObjectType());
        }
 
        /**
         * @test
-        * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
         */
-       public function updateRejectsObjectsOfWrongType() {
-               $this->repository->_set('objectType', 'Foo');
-               $this->repository->update(new \stdClass());
+       public function createQueryReturnsQueryWithUnmodifiedDefaultQuerySettings() {
+               $this->mockQuery = new \TYPO3\CMS\Extbase\Persistence\Generic\Query('foo');
+               $mockDefaultQuerySettings = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
+               $this->repository->setDefaultQuerySettings($mockDefaultQuerySettings);
+               $query = $this->repository->createQuery();
+               $instanceQuerySettings = $query->getQuerySettings();
+               $this->assertEquals($mockDefaultQuerySettings, $instanceQuerySettings);
+               $this->assertNotSame($mockDefaultQuerySettings, $instanceQuerySettings);
        }
 
        /**
         * @test
         */
-       public function magicCallMethodAcceptsFindBySomethingCallsAndExecutesAQueryWithThatCriteria() {
-               $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
-               $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
-               $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
-               $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
-               $this->assertSame($mockQueryResult, $this->repository->findByFoo('bar'));
+       public function findByUidReturnsResultOfGetObjectByIdentifierCall() {
+               $fakeUid = '123';
+               $object = new \stdClass();
+               $this->repository->_set('objectType', 'someObjectType');
+               $this->mockPersistenceManager->expects($this->once())->method('getObjectByIdentifier')->with($fakeUid, 'someObjectType')->will($this->returnValue($object));
+               $expectedResult = $object;
+               $actualResult = $this->repository->findByUid($fakeUid);
+               $this->assertSame($expectedResult, $actualResult);
        }
 
        /**
         * @test
+        * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
         */
-       public function magicCallMethodAcceptsFindOneBySomethingCallsAndExecutesAQueryWithThatCriteria() {
-               $object = new \stdClass();
-               $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
-               $mockQueryResult->expects($this->once())->method('getFirst')->will($this->returnValue($object));
-               $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
-               $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
-               $this->mockQuery->expects($this->once())->method('setLimit')->with(1)->will($this->returnValue($this->mockQuery));
-               $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
-               $this->assertSame($object, $this->repository->findOneByFoo('bar'));
+       public function updateRejectsObjectsOfWrongType() {
+               $this->repository->_set('objectType', 'Foo');
+               $this->repository->update(new \stdClass());
        }
 
        /**
@@ -404,43 +394,6 @@ class RepositoryTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
                $this->assertNull($this->repository->findOneByFoo('bar'));
        }
 
-       /**
-        * @test
-        */
-       public function magicCallMethodAcceptsCountBySomethingCallsAndExecutesAQueryWithThatCriteria() {
-               $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
-               $mockQueryResult->expects($this->once())->method('count')->will($this->returnValue(2));
-               $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
-               $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
-               $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
-               $this->assertSame(2, $this->repository->countByFoo('bar'));
-       }
-
-       /**
-        * @test
-        * @expectedException \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedMethodException
-        */
-       public function magicCallMethodTriggersAnErrorIfUnknownMethodsAreCalled() {
-               $this->repository->__call('foo', array());
-       }
-
-       /**
-        * @test
-        * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
-        */
-       public function addChecksObjectType() {
-               $this->repository->_set('objectType', 'ExpectedObjectType');
-               $this->repository->add(new \stdClass());
-       }
-
-       /**
-        * @test
-        * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
-        */
-       public function removeChecksObjectType() {
-               $this->repository->_set('objectType', 'ExpectedObjectType');
-               $this->repository->remove(new \stdClass());
-       }
 }
 
 ?>
\ No newline at end of file