diff --git a/DependencyInjection/FOQElasticaExtension.php b/DependencyInjection/FOQElasticaExtension.php index 163e4ae..f4c0636 100644 --- a/DependencyInjection/FOQElasticaExtension.php +++ b/DependencyInjection/FOQElasticaExtension.php @@ -316,14 +316,8 @@ class FOQElasticaExtension extends Extension $listenerDef = new DefinitionDecorator($abstractListenerId); $listenerDef->replaceArgument(0, new Reference($objectPersisterId)); $listenerDef->replaceArgument(1, $typeConfig['model']); - $events = array(); - $doctrineEvents = array('insert' => 'postPersist', 'update' => 'postUpdate', 'delete' => 'postRemove'); - foreach ($doctrineEvents as $event => $doctrineEvent) { - if (isset($typeConfig['listener'][$event]) && $typeConfig['listener'][$event]) { - $events[] = $doctrineEvent; - } - } - $listenerDef->replaceArgument(2, $events); + $listenerDef->replaceArgument(3, $typeConfig['identifier']); + $listenerDef->replaceArgument(2, $this->getDoctrineEvents($typeConfig)); switch ($typeConfig['driver']) { case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break; case 'mongodb': $listenerDef->addTag('doctrine.common.event_subscriber'); break; @@ -333,6 +327,24 @@ class FOQElasticaExtension extends Extension return $listenerId; } + private function getDoctrineEvents(array $typeConfig) + { + $events = array(); + $eventMapping = array( + 'insert' => array('postPersist'), + 'update' => array('postUpdate'), + 'delete' => array('postRemove', 'preRemove') + ); + + foreach ($eventMapping as $event => $doctrineEvents) { + if (isset($typeConfig['listener'][$event]) && $typeConfig['listener'][$event]) { + $events = array_merge($events, $doctrineEvents); + } + } + + return $events; + } + protected function loadTypeFinder(array $typeConfig, ContainerBuilder $container, $elasticaToModelId, $typeDef, $indexName, $typeName) { if (isset($typeConfig['finder']['service'])) { diff --git a/Doctrine/AbstractListener.php b/Doctrine/AbstractListener.php index 343f9c2..548ccc6 100644 --- a/Doctrine/AbstractListener.php +++ b/Doctrine/AbstractListener.php @@ -28,14 +28,19 @@ abstract class AbstractListener */ protected $events; + protected $esIdentifierField; + protected $scheduledForRemoval; + /** * Constructor **/ - public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events) + public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id') { - $this->objectPersister = $objectPersister; - $this->objectClass = $objectClass; - $this->events = $events; + $this->objectPersister = $objectPersister; + $this->objectClass = $objectClass; + $this->events = $events; + $this->esIdentifierField = $esIdentifierField; + $this->scheduledForRemoval = array(); } /** @@ -46,4 +51,19 @@ abstract class AbstractListener return $this->events; } + protected function scheduleForRemoval($object, $objectManager) + { + $metadata = $objectManager->getClassMetadata($this->objectClass); + $esId = $metadata->getFieldValue($object, $this->esIdentifierField); + $this->scheduledForRemoval[spl_object_hash($object)] = $esId; + } + + protected function removeIfScheduled($object) + { + $objectHash = spl_object_hash($object); + if (isset($this->scheduledForRemoval[$objectHash])) { + $this->objectPersister->deleteById($this->scheduledForRemoval[$objectHash]); + unset($this->scheduledForRemoval[$objectHash]); + } + } } diff --git a/Doctrine/MongoDB/Listener.php b/Doctrine/MongoDB/Listener.php index 89c7502..5853e0f 100644 --- a/Doctrine/MongoDB/Listener.php +++ b/Doctrine/MongoDB/Listener.php @@ -26,12 +26,21 @@ class Listener extends AbstractListener implements EventSubscriber } } + public function preRemove(LifecycleEventArgs $eventArgs) + { + $document = $eventArgs->getDocument(); + + if ($document instanceof $this->objectClass) { + $this->scheduleForRemoval($document, $eventArgs->getDocumentManager()); + } + } + public function postRemove(LifecycleEventArgs $eventArgs) { $document = $eventArgs->getDocument(); if ($document instanceof $this->objectClass) { - $this->objectPersister->deleteOne($document); + $this->removeIfScheduled($document); } } } diff --git a/Doctrine/ORM/Listener.php b/Doctrine/ORM/Listener.php index 7e5bea8..57318b9 100644 --- a/Doctrine/ORM/Listener.php +++ b/Doctrine/ORM/Listener.php @@ -26,12 +26,21 @@ class Listener extends AbstractListener implements EventSubscriber } } + public function preRemove(LifecycleEventArgs $eventArgs) + { + $entity = $eventArgs->getEntity(); + + if ($entity instanceof $this->objectClass) { + $this->scheduleForRemoval($entity, $eventArgs->getEntityManager()); + } + } + public function postRemove(LifecycleEventArgs $eventArgs) { $entity = $eventArgs->getEntity(); if ($entity instanceof $this->objectClass) { - $this->objectPersister->deleteOne($entity); + $this->removeIfScheduled($entity); } } } diff --git a/Persister/ObjectPersister.php b/Persister/ObjectPersister.php index 87653f4..47bbbe5 100644 --- a/Persister/ObjectPersister.php +++ b/Persister/ObjectPersister.php @@ -67,6 +67,19 @@ class ObjectPersister implements ObjectPersisterInterface $this->type->deleteById($document->getId()); } + /** + * Deletes one object in the type by id + * + * @param mixed $id + * + * @return null + **/ + public function deleteById($id) + { + $this->type->deleteById($id); + } + + /** * Inserts an array of objects in the type * @@ -91,5 +104,4 @@ class ObjectPersister implements ObjectPersisterInterface { return $this->transformer->transform($object, $this->fields); } - } diff --git a/Persister/ObjectPersisterInterface.php b/Persister/ObjectPersisterInterface.php index 3cf12d2..962a88c 100644 --- a/Persister/ObjectPersisterInterface.php +++ b/Persister/ObjectPersisterInterface.php @@ -32,6 +32,15 @@ interface ObjectPersisterInterface **/ function deleteOne($object); + /** + * Deletes one object in the type by id + * + * @param mixed $id + * + * @return null + **/ + function deleteById($id); + /** * Inserts an array of objects in the type * diff --git a/Resources/config/mongodb.xml b/Resources/config/mongodb.xml index 5a95da3..a1bd8d3 100644 --- a/Resources/config/mongodb.xml +++ b/Resources/config/mongodb.xml @@ -18,6 +18,7 @@ + diff --git a/Resources/config/orm.xml b/Resources/config/orm.xml index 059a002..3803853 100644 --- a/Resources/config/orm.xml +++ b/Resources/config/orm.xml @@ -18,6 +18,7 @@ + diff --git a/Tests/Doctrine/MongoDB/ListenerTest.php b/Tests/Doctrine/MongoDB/ListenerTest.php index eff42d7..0b664dd 100644 --- a/Tests/Doctrine/MongoDB/ListenerTest.php +++ b/Tests/Doctrine/MongoDB/ListenerTest.php @@ -11,7 +11,6 @@ class Document{} */ class ListenerTest extends \PHPUnit_Framework_TestCase { - public function testObjectInsertedOnPersist() { $persisterMock = $this->getMockBuilder('FOQ\ElasticaBundle\Persister\ObjectPersisterInterface') @@ -33,7 +32,7 @@ class ListenerTest extends \PHPUnit_Framework_TestCase ->method('insertOne') ->with($this->equalTo($document)); - $listener = new Listener($persisterMock, $objectName, array(), null); + $listener = new Listener($persisterMock, $objectName, array()); $listener->postPersist($eventArgsMock); } @@ -58,7 +57,7 @@ class ListenerTest extends \PHPUnit_Framework_TestCase ->method('replaceOne') ->with($this->equalTo($document)); - $listener = new Listener($persisterMock, $objectName, array(), null); + $listener = new Listener($persisterMock, $objectName, array()); $listener->postUpdate($eventArgsMock); } @@ -72,19 +71,90 @@ class ListenerTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); - $objectName = 'FOQ\ElasticaBundle\Tests\Doctrine\MongoDB\Document'; - $document = new Document(); + $documentManagerMock = $this->getMockBuilder('Doctrine\ODM\MongoDB\DocumentManager') + ->disableOriginalConstructor() + ->getMock(); - $eventArgsMock->expects($this->once()) + $metadataMock = $this->getMockBuilder('Doctrine\ODM\MongoDB\Mapping\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + + $objectName = 'FOQ\ElasticaBundle\Tests\Doctrine\MongoDB\Document'; + $document = new Document(); + $documentId = 78; + + $eventArgsMock->expects($this->any()) ->method('getDocument') ->will($this->returnValue($document)); - $persisterMock->expects($this->once()) - ->method('deleteOne') - ->with($this->equalTo($document)); + $eventArgsMock->expects($this->any()) + ->method('getDocumentManager') + ->will($this->returnValue($documentManagerMock)); - $listener = new Listener($persisterMock, $objectName, array(), null); + $documentManagerMock->expects($this->any()) + ->method('getClassMetadata') + ->will($this->returnValue($metadataMock)); + + $metadataMock->expects($this->any()) + ->method('getFieldValue') + ->with($this->equalTo($document), $this->equalTo('id')) + ->will($this->returnValue($documentId)); + + $persisterMock->expects($this->once()) + ->method('deleteById') + ->with($this->equalTo($documentId)); + + $listener = new Listener($persisterMock, $objectName, array()); + $listener->preRemove($eventArgsMock); $listener->postRemove($eventArgsMock); } + public function testObjectWithNonStandardIdentifierDeletedOnRemove() + { + $persisterMock = $this->getMockBuilder('FOQ\ElasticaBundle\Persister\ObjectPersisterInterface') + ->disableOriginalConstructor() + ->getMock(); + + $eventArgsMock = $this->getMockBuilder('Doctrine\ODM\MongoDB\Event\LifecycleEventArgs') + ->disableOriginalConstructor() + ->getMock(); + + $documentManagerMock = $this->getMockBuilder('Doctrine\ODM\MongoDB\DocumentManager') + ->disableOriginalConstructor() + ->getMock(); + + $metadataMock = $this->getMockBuilder('Doctrine\ODM\MongoDB\Mapping\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + + $objectName = 'FOQ\ElasticaBundle\Tests\Doctrine\MongoDB\Document'; + $document = new Document(); + $documentIdentifier = 826; + $identifierField = 'identifier'; + + $eventArgsMock->expects($this->any()) + ->method('getDocument') + ->will($this->returnValue($document)); + + $eventArgsMock->expects($this->any()) + ->method('getDocumentManager') + ->will($this->returnValue($documentManagerMock)); + + $documentManagerMock->expects($this->any()) + ->method('getClassMetadata') + ->will($this->returnValue($metadataMock)); + + $metadataMock->expects($this->any()) + ->method('getFieldValue') + ->with($this->equalTo($document), $this->equalTo($identifierField)) + ->will($this->returnValue($documentIdentifier)); + + $persisterMock->expects($this->once()) + ->method('deleteById') + ->with($this->equalTo($documentIdentifier)); + + $listener = new Listener($persisterMock, $objectName, array(), 'identifier'); + $listener->preRemove($eventArgsMock); + $listener->postRemove($eventArgsMock); + } } diff --git a/Tests/Doctrine/ORM/ListenerTest.php b/Tests/Doctrine/ORM/ListenerTest.php index ac733f7..9a632c3 100644 --- a/Tests/Doctrine/ORM/ListenerTest.php +++ b/Tests/Doctrine/ORM/ListenerTest.php @@ -11,7 +11,6 @@ class Entity{} */ class ListenerTest extends \PHPUnit_Framework_TestCase { - public function testObjectInsertedOnPersist() { $persisterMock = $this->getMockBuilder('FOQ\ElasticaBundle\Persister\ObjectPersisterInterface') @@ -33,7 +32,7 @@ class ListenerTest extends \PHPUnit_Framework_TestCase ->method('insertOne') ->with($this->equalTo($entity)); - $listener = new Listener($persisterMock, $objectName, array(), null); + $listener = new Listener($persisterMock, $objectName, array()); $listener->postPersist($eventArgsMock); } @@ -58,7 +57,7 @@ class ListenerTest extends \PHPUnit_Framework_TestCase ->method('replaceOne') ->with($this->equalTo($entity)); - $listener = new Listener($persisterMock, $objectName, array(), null); + $listener = new Listener($persisterMock, $objectName, array()); $listener->postUpdate($eventArgsMock); } @@ -72,19 +71,90 @@ class ListenerTest extends \PHPUnit_Framework_TestCase ->disableOriginalConstructor() ->getMock(); + $entityManagerMock = $this->getMockBuilder('Doctrine\ORM\EntityManager') + ->disableOriginalConstructor() + ->getMock(); + + $metadataMock = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + $objectName = 'FOQ\ElasticaBundle\Tests\Doctrine\ORM\Entity'; $entity = new Entity; + $entityId = 21; - $eventArgsMock->expects($this->once()) + $eventArgsMock->expects($this->any()) ->method('getEntity') ->will($this->returnValue($entity)); - $persisterMock->expects($this->once()) - ->method('deleteOne') - ->with($this->equalTo($entity)); + $eventArgsMock->expects($this->any()) + ->method('getEntityManager') + ->will($this->returnValue($entityManagerMock)); - $listener = new Listener($persisterMock, $objectName, array(), null); + $entityManagerMock->expects($this->any()) + ->method('getClassMetadata') + ->will($this->returnValue($metadataMock)); + + $metadataMock->expects($this->any()) + ->method('getFieldValue') + ->with($this->equalTo($entity), $this->equalTo('id')) + ->will($this->returnValue($entityId)); + + $persisterMock->expects($this->once()) + ->method('deleteById') + ->with($this->equalTo($entityId)); + + $listener = new Listener($persisterMock, $objectName, array()); + $listener->preRemove($eventArgsMock); $listener->postRemove($eventArgsMock); } + public function testObjectWithNonStandardIdentifierDeletedOnRemove() + { + $persisterMock = $this->getMockBuilder('FOQ\ElasticaBundle\Persister\ObjectPersisterInterface') + ->disableOriginalConstructor() + ->getMock(); + + $eventArgsMock = $this->getMockBuilder('Doctrine\ORM\Event\LifecycleEventArgs') + ->disableOriginalConstructor() + ->getMock(); + + $entityManagerMock = $this->getMockBuilder('Doctrine\ORM\EntityManager') + ->disableOriginalConstructor() + ->getMock(); + + $metadataMock = $this->getMockBuilder('Doctrine\ORM\Mapping\ClassMetadata') + ->disableOriginalConstructor() + ->getMock(); + + $objectName = 'FOQ\ElasticaBundle\Tests\Doctrine\ORM\Entity'; + $entity = new Entity; + $entityIdentifier = 924; + $identifierField = 'identifier'; + + $eventArgsMock->expects($this->any()) + ->method('getEntity') + ->will($this->returnValue($entity)); + + $eventArgsMock->expects($this->any()) + ->method('getEntityManager') + ->will($this->returnValue($entityManagerMock)); + + $entityManagerMock->expects($this->any()) + ->method('getClassMetadata') + ->will($this->returnValue($metadataMock)); + + $metadataMock->expects($this->any()) + ->method('getFieldValue') + ->with($this->equalTo($entity), $this->equalTo($identifierField)) + ->will($this->returnValue($entityIdentifier)); + + $persisterMock->expects($this->once()) + ->method('deleteById') + ->with($this->equalTo($entityIdentifier)); + + $listener = new Listener($persisterMock, $objectName, array(), $identifierField); + $listener->preRemove($eventArgsMock); + $listener->postRemove($eventArgsMock); + } }