Refactor to a single Listener class. Update tests.
This commit is contained in:
parent
5ec652063d
commit
1d700261ab
|
@ -1,228 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use Doctrine\Common\EventSubscriber;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
||||
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\SyntaxError;
|
||||
|
||||
abstract class AbstractListener implements EventSubscriber
|
||||
{
|
||||
/**
|
||||
* Object persister
|
||||
*
|
||||
* @var ObjectPersister
|
||||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* Class of the domain model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* List of subscribed events
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $events;
|
||||
|
||||
/**
|
||||
* Name of domain model field used as the ES identifier
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $esIdentifierField;
|
||||
|
||||
/**
|
||||
* Callback for determining if an object should be indexed
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $isIndexableCallback;
|
||||
|
||||
/**
|
||||
* Objects scheduled for insertion, replacement, or removal
|
||||
*/
|
||||
protected $scheduledForInsertion = array();
|
||||
protected $scheduledForUpdate = array();
|
||||
protected $scheduledForDeletion = array();
|
||||
|
||||
/**
|
||||
* An instance of ExpressionLanguage
|
||||
*
|
||||
* @var ExpressionLanguage
|
||||
*/
|
||||
protected $expressionLanguage;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param string $objectClass
|
||||
* @param array $events
|
||||
* @param string $esIdentifierField
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id')
|
||||
{
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->events = $events;
|
||||
$this->esIdentifierField = $esIdentifierField;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Doctrine\Common\EventSubscriber::getSubscribedEvents()
|
||||
*/
|
||||
public function getSubscribedEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback for determining object index eligibility.
|
||||
*
|
||||
* If callback is a string, it must be public method on the object class
|
||||
* that expects no arguments and returns a boolean. Otherwise, the callback
|
||||
* should expect the object for consideration as its only argument and
|
||||
* return a boolean.
|
||||
*
|
||||
* @param callback $callback
|
||||
* @throws \RuntimeException if the callback is not callable
|
||||
*/
|
||||
public function setIsIndexableCallback($callback)
|
||||
{
|
||||
if (is_string($callback)) {
|
||||
if (!is_callable(array($this->objectClass, $callback))) {
|
||||
if (false !== ($expression = $this->getExpressionLanguage())) {
|
||||
$callback = new Expression($callback);
|
||||
try {
|
||||
$expression->compile($callback, array($this->getExpressionVar()));
|
||||
} catch (SyntaxError $e) {
|
||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable or a valid expression.', $this->objectClass, $callback), 0, $e);
|
||||
}
|
||||
} else {
|
||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $this->objectClass, $callback));
|
||||
}
|
||||
}
|
||||
} elseif (!is_callable($callback)) {
|
||||
if (is_array($callback)) {
|
||||
list($class, $method) = $callback + array(null, null);
|
||||
if (is_object($class)) {
|
||||
$class = get_class($class);
|
||||
}
|
||||
|
||||
if ($class && $method) {
|
||||
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $class, $method));
|
||||
}
|
||||
}
|
||||
throw new \RuntimeException('Indexable callback is not callable.');
|
||||
}
|
||||
|
||||
$this->isIndexableCallback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the object is indexable with respect to the callback.
|
||||
*
|
||||
* @param object $object
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isObjectIndexable($object)
|
||||
{
|
||||
if (!$this->isIndexableCallback) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isIndexableCallback instanceof Expression) {
|
||||
return $this->getExpressionLanguage()->evaluate($this->isIndexableCallback, array($this->getExpressionVar($object) => $object));
|
||||
}
|
||||
|
||||
return is_string($this->isIndexableCallback)
|
||||
? call_user_func(array($object, $this->isIndexableCallback))
|
||||
: call_user_func($this->isIndexableCallback, $object);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $object
|
||||
* @return string
|
||||
*/
|
||||
private function getExpressionVar($object = null)
|
||||
{
|
||||
$class = $object ?: $this->objectClass;
|
||||
$ref = new \ReflectionClass($class);
|
||||
|
||||
return strtolower($ref->getShortName());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|ExpressionLanguage
|
||||
*/
|
||||
private function getExpressionLanguage()
|
||||
{
|
||||
if (null === $this->expressionLanguage) {
|
||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->expressionLanguage = new ExpressionLanguage();
|
||||
}
|
||||
|
||||
return $this->expressionLanguage;
|
||||
}
|
||||
|
||||
public function postPersist(EventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getEntity();
|
||||
|
||||
if ($entity instanceof $this->objectClass && $this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForInsertion[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
public function postUpdate(EventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getEntity();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
if ($this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForUpdate[] = $entity;
|
||||
} else {
|
||||
// Delete if no longer indexable
|
||||
$this->scheduledForDeletion[] = $entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function preRemove(EventArgs $eventArgs)
|
||||
{
|
||||
$entity = $eventArgs->getEntity();
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
$this->scheduledForDeletion[] = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate through scheduled actions *after* flushing to ensure that the ElasticSearch index will only be affected
|
||||
* only if the query is successful
|
||||
*/
|
||||
public function postFlush(EventArgs $eventArgs)
|
||||
{
|
||||
foreach ($this->scheduledForInsertion as $entity) {
|
||||
$this->objectPersister->insertOne($entity);
|
||||
}
|
||||
foreach ($this->scheduledForUpdate as $entity) {
|
||||
$this->objectPersister->replaceOne($entity);
|
||||
}
|
||||
foreach ($this->scheduledForDeletion as $entity) {
|
||||
$this->objectPersister->deleteOne($entity);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\MongoDB;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractListener;
|
||||
|
||||
class Listener extends AbstractListener
|
||||
{
|
||||
public function postPersist(EventArgs $eventArgs)
|
||||
{
|
||||
parent::postPersist($eventArgs);
|
||||
}
|
||||
|
||||
public function postUpdate(EventArgs $eventArgs)
|
||||
{
|
||||
parent::postUpdate($eventArgs);
|
||||
}
|
||||
|
||||
public function postRemove(EventArgs $eventArgs)
|
||||
{
|
||||
parent::postRemove($eventArgs);
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace FOS\ElasticaBundle\Doctrine\ORM;
|
||||
|
||||
use Doctrine\Common\EventArgs;
|
||||
use FOS\ElasticaBundle\Doctrine\AbstractListener;
|
||||
|
||||
class Listener extends AbstractListener
|
||||
{
|
||||
public function postPersist(EventArgs $eventArgs)
|
||||
{
|
||||
parent::postPersist($eventArgs);
|
||||
}
|
||||
|
||||
public function postUpdate(EventArgs $eventArgs)
|
||||
{
|
||||
parent::postUpdate($eventArgs);
|
||||
}
|
||||
|
||||
public function preRemove(EventArgs $eventArgs)
|
||||
{
|
||||
parent::preRemove($eventArgs);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
<argument type="service" id="doctrine_mongodb" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.listener.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\Listener" public="false" abstract="true">
|
||||
<service id="fos_elastica.listener.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\Listener" public="false">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<argument type="service" id="doctrine" />
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.listener.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\Listener" public="false" abstract="true">
|
||||
<service id="fos_elastica.listener.prototype.orm" class="FOS\ElasticaBundle\Doctrine\Listener" public="false">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
namespace FOS\ElasticaBundle\Tests\Doctrine;
|
||||
|
||||
/**
|
||||
* See concrete MongoDB/ORM instances of this abstract test
|
||||
*
|
||||
* @author Richard Miller <info@limethinking.co.uk>
|
||||
*/
|
||||
abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
|
||||
abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testObjectInsertedOnPersist()
|
||||
{
|
||||
|
@ -14,12 +16,16 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$entity = new Listener\Entity(1);
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForInsertion));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('insertOne')
|
||||
->with($entity);
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->postPersist($eventArgs);
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,12 +38,16 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$entity = new Listener\Entity(1, false);
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
|
||||
$persister->expects($this->never())
|
||||
->method('insertOne');
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForInsertion);
|
||||
|
||||
$persister->expects($this->never())
|
||||
->method('insertOne');
|
||||
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
public function testObjectReplacedOnUpdate()
|
||||
|
@ -47,15 +57,18 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$entity = new Listener\Entity(1);
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForUpdate));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('replaceOne')
|
||||
->with($entity);
|
||||
|
||||
$persister->expects($this->never())
|
||||
->method('deleteById');
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->postUpdate($eventArgs);
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,16 +93,20 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$persister->expects($this->never())
|
||||
->method('replaceOne');
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('deleteById')
|
||||
->with($entity->getId());
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForUpdate);
|
||||
$this->assertEquals($entity, current($listener->scheduledForDeletion));
|
||||
|
||||
$persister->expects($this->never())
|
||||
->method('replaceOne');
|
||||
$persister->expects($this->once())
|
||||
->method('deleteOne')
|
||||
->with($entity);
|
||||
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
public function testObjectDeletedOnRemove()
|
||||
|
@ -111,13 +128,16 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('deleteById')
|
||||
->with($entity->getId());
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->preRemove($eventArgs);
|
||||
$listener->postRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForDeletion));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('deleteOne')
|
||||
->with($entity);
|
||||
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
public function testObjectWithNonStandardIdentifierDeletedOnRemove()
|
||||
|
@ -139,13 +159,16 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'identifier')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('deleteById')
|
||||
->with($entity->getId());
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array(), 'identifier');
|
||||
$listener->preRemove($eventArgs);
|
||||
$listener->postRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForDeletion));
|
||||
|
||||
$persister->expects($this->once())
|
||||
->method('deleteOne')
|
||||
->with($entity);
|
||||
|
||||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Tests\Doctrine\MongoDB;
|
||||
|
||||
use FOS\ElasticaBundle\Tests\Doctrine\AbstractListenerTest;
|
||||
use FOS\ElasticaBundle\Tests\Doctrine\ListenerTest as BaseListenerTest;
|
||||
|
||||
class ListenerTest extends AbstractListenerTest
|
||||
class ListenerTest extends BaseListenerTest
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ class ListenerTest extends AbstractListenerTest
|
|||
|
||||
protected function getListenerClass()
|
||||
{
|
||||
return 'FOS\ElasticaBundle\Doctrine\MongoDB\Listener';
|
||||
return 'FOS\ElasticaBundle\Doctrine\Listener';
|
||||
}
|
||||
|
||||
protected function getObjectManagerClass()
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Tests\Doctrine\ORM;
|
||||
|
||||
use FOS\ElasticaBundle\Tests\Doctrine\AbstractListenerTest;
|
||||
use FOS\ElasticaBundle\Tests\Doctrine\ListenerTest as BaseListenerTest;
|
||||
|
||||
class ListenerTest extends AbstractListenerTest
|
||||
class ListenerTest extends BaseListenerTest
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
|
@ -25,7 +25,7 @@ class ListenerTest extends AbstractListenerTest
|
|||
|
||||
protected function getListenerClass()
|
||||
{
|
||||
return 'FOS\ElasticaBundle\Doctrine\ORM\Listener';
|
||||
return 'FOS\ElasticaBundle\Doctrine\Listener';
|
||||
}
|
||||
|
||||
protected function getObjectManagerClass()
|
||||
|
|
Loading…
Reference in a new issue