diff --git a/Classes/Package.php b/Classes/Package.php deleted file mode 100644 index 8a1fc6f..0000000 --- a/Classes/Package.php +++ /dev/null @@ -1,37 +0,0 @@ -getSignalSlotDispatcher(); - $dispatcher->connect(Bootstrap::class, 'finishedRuntimeRun', DoctrineProjectionPersistenceManager::class, 'persistAll'); - } -} diff --git a/Classes/Projection/Doctrine/AbstractDoctrineFinder.php b/Classes/Projection/Doctrine/AbstractDoctrineFinder.php index d8a1ed6..1b10f19 100644 --- a/Classes/Projection/Doctrine/AbstractDoctrineFinder.php +++ b/Classes/Projection/Doctrine/AbstractDoctrineFinder.php @@ -22,11 +22,6 @@ */ abstract class AbstractDoctrineFinder { - /** - * @Flow\Inject - * @var DoctrineProjectionPersistenceManager - */ - protected $projectionPersistenceManager; /** * Concrete projectors may override this property for setting the default sorting order of query results. diff --git a/Classes/Projection/Doctrine/AbstractDoctrineProjector.php b/Classes/Projection/Doctrine/AbstractDoctrineProjector.php index ca6dfb0..1bfa638 100644 --- a/Classes/Projection/Doctrine/AbstractDoctrineProjector.php +++ b/Classes/Projection/Doctrine/AbstractDoctrineProjector.php @@ -12,7 +12,7 @@ */ use Neos\EventSourcing\Exception; -use Neos\EventSourcing\Projection\AbstractBaseProjector; +use Neos\EventSourcing\Projection\ProjectorInterface; use Neos\Flow\Annotations as Flow; /** @@ -20,103 +20,77 @@ * * @api */ -abstract class AbstractDoctrineProjector extends AbstractBaseProjector +abstract class AbstractDoctrineProjector implements ProjectorInterface { /** * @Flow\Inject - * @var DoctrineProjectionPersistenceManager + * @var DoctrineProjectionState */ protected $projectionPersistenceManager; /** - * Initialize the Read Model class name - * Make sure to call this method as parent when overriding it in a concrete projector. - * - * @return void - * @throws Exception - */ - protected function initializeObject() - { - if ($this->readModelClassName === null) { - if (substr(get_class($this), -9, 9) !== 'Projector') { - throw new Exception(sprintf('The class name "%s" doesn\'t end in "Projector" so the Read Model class name can\'t be determined automatically. Please set the "readModelClassName" field it in your concrete projector.', get_class($this)), 1476799474); - } - $this->readModelClassName = substr(get_class($this), 0, -9); - } - } - - /** - * Retrieves an object with the given $identifier. - * For use in the concrete projector. + * Concrete projectors may override this property for setting the class name of the Read Model to a non-conventional name * - * @param mixed $identifier - * @return object an instance of $this->readModelClassName or NULL if no matching object could be found + * @var string * @api */ - public function get($identifier) - { - return $this->projectionPersistenceManager->get($this->getReadModelClassName(), $identifier); - } + protected $readModelClassName; /** - * Adds an object to this repository. - * For use in the concrete projector. + * The state of this projection * - * @param object $object The object to add - * @return void + * @var DoctrineProjectionState * @api */ - protected function add($object) - { - $this->projectionPersistenceManager->add($object); - } + protected $state; /** - * Schedules a modified object for persistence. - * For use in the concrete projector. + * Returns the class name of the (main) Read Model of the concrete projector * - * @param object $object The modified object - * @return void - * @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException - * @api + * @return string */ - protected function update($object) + public function getReadModelClassName(): string { - $this->projectionPersistenceManager->update($object); + return $this->readModelClassName; } /** - * Removes an object from the projector's persistence. - * For use in the concrete projector. + * Initialize the Read Model class name + * Make sure to call this method as parent when overriding it in a concrete projector. * - * @param object $object The object to remove * @return void - * @api + * @throws Exception */ - protected function remove($object) + protected function initializeObject() { - $this->projectionPersistenceManager->remove($object); + if ($this->readModelClassName === null) { + if (substr(get_class($this), -9, 9) !== 'Projector') { + throw new Exception(sprintf('The class name "%s" doesn\'t end in "Projector" so the Read Model class name can\'t be determined automatically. Please set the "readModelClassName" field it in your concrete projector.', get_class($this)), 1476799474); + } + $this->readModelClassName = substr(get_class($this), 0, -9); + } + $this->state = new DoctrineProjectionState($this->readModelClassName); } /** - * Removes all objects of this repository as if remove() was called for all of them. - * For usage in the concrete projector. + * @inheritdoc * * @return void * @api */ public function reset() { - $this->projectionPersistenceManager->drop($this->getReadModelClassName()); + $this->state->reset(); } /** - * If this projection is currently empty + * @inheritdoc * * @return bool */ public function isEmpty() { - return $this->projectionPersistenceManager->count($this->getReadModelClassName()) === 0; + return $this->state->isEmpty(); } + } diff --git a/Classes/Projection/Doctrine/DoctrineProjectionPersistenceManager.php b/Classes/Projection/Doctrine/DoctrineProjectionState.php similarity index 75% rename from Classes/Projection/Doctrine/DoctrineProjectionPersistenceManager.php rename to Classes/Projection/Doctrine/DoctrineProjectionState.php index 25844be..20e178f 100644 --- a/Classes/Projection/Doctrine/DoctrineProjectionPersistenceManager.php +++ b/Classes/Projection/Doctrine/DoctrineProjectionState.php @@ -20,12 +20,11 @@ use Neos\Flow\Annotations as Flow; /** - * A persistence manager for Doctrine-based projectors + * A state for Doctrine-based projectors * - * @Flow\Scope("singleton") * @api */ -class DoctrineProjectionPersistenceManager +class DoctrineProjectionState { /** @@ -44,6 +43,19 @@ class DoctrineProjectionPersistenceManager */ private $numberOfPendingChanges = 0; + /** + * @var string + */ + private $readModelClassName; + + /** + * @param string $readModelClassName + */ + public function __construct($readModelClassName) + { + $this->readModelClassName = $readModelClassName; + } + /** * @param DoctrineObjectManager $entityManager * @return void @@ -54,22 +66,25 @@ public function injectEntityManager(DoctrineObjectManager $entityManager) } /** - * Returns an object with the given $identifier from persistence. + * Retrieves an object with the given $identifier. + * For use in the concrete projector. * - * @param string $className - * @param mixed $identifier - * @return object + * @param string $identifier + * @return object an instance of $this->readModelClassName or NULL if no matching object could be found + * @api */ - public function get(string $className, $identifier) + public function get($identifier) { - return $this->entityManager->find($className, $identifier); + return $this->entityManager->find($this->readModelClassName, $identifier); } /** - * Adds an object for persistence. + * Adds an object to this repository. + * For use in the concrete projector. * * @param object $object The object to add * @return void + * @api */ public function add($object) { @@ -79,9 +94,12 @@ public function add($object) /** * Schedules a modified object for persistence. + * For use in the concrete projector. * * @param object $object The modified object + * @return void * @throws Exception + * @api */ public function update($object) { @@ -100,10 +118,12 @@ public function update($object) } /** - * Removes an object from this repository. + * Removes an object from the projector's persistence. + * For use in the concrete projector. * * @param object $object The object to remove * @return void + * @api */ public function remove($object) { @@ -112,27 +132,28 @@ public function remove($object) } /** - * Removes all objects from a Doctrine-based projection. + * Removes all objects of this repository as if remove() was called for all of them. + * For usage in the concrete projector. * - * @param string $readModelClassName Read Model class name of the projection - * @return int Number of records which have been deleted + * @return void + * @api */ - public function drop(string $readModelClassName): int + public function reset() { - $query = $this->entityManager->createQuery('DELETE FROM ' . $readModelClassName); - return $query->execute(); + $query = $this->entityManager->createQuery('DELETE FROM ' . $this->readModelClassName); + $query->execute(); } /** - * Returns the number of read models stored in this projection. + * If this projection is currently empty * - * @param string $readModelClassName - * @return int + * @return bool + * @api */ - public function count(string $readModelClassName): int + public function isEmpty(): bool { - $query = $this->entityManager->createQuery('SELECT COUNT(m) FROM ' . $readModelClassName . ' m'); - return $query->getSingleScalarResult(); + $query = $this->entityManager->createQuery('SELECT COUNT(m) FROM ' . $this->readModelClassName . ' m'); + return $query->getSingleScalarResult() === 0; } /** @@ -187,4 +208,12 @@ public function isNewObject($object): bool { return ($this->entityManager->getUnitOfWork()->getEntityState($object, UnitOfWork::STATE_NEW) === UnitOfWork::STATE_NEW); } + + /** + * @return void + */ + public function shutdownObject() + { + $this->persistAll(); + } } diff --git a/Classes/Projection/ProjectorInterface.php b/Classes/Projection/ProjectorInterface.php index 14e8b83..df132bb 100644 --- a/Classes/Projection/ProjectorInterface.php +++ b/Classes/Projection/ProjectorInterface.php @@ -27,7 +27,7 @@ interface ProjectorInterface extends EventListenerInterface public function getReadModelClassName(): string; /** - * Removes all objects of this repository as if remove() was called for all of them. + * Removes all data of this projections state as if remove() was called for all of them. * For usage in the concrete projector. * * @return void @@ -36,7 +36,7 @@ public function getReadModelClassName(): string; public function reset(); /** - * Returns true if the projection maintained by the concreted projector does not contain any data (yet). + * Returns true if the projection state maintained by the concrete projector does not contain any data (yet). * * @return boolean */