Move Indexable callback calculations to a new service
This commit is contained in:
parent
14083496d7
commit
66d2410999
|
@ -187,9 +187,23 @@ class Configuration implements ConfigurationInterface
|
|||
return $v;
|
||||
})
|
||||
->end()
|
||||
->beforeNormalization()
|
||||
->ifTrue(function ($v) {
|
||||
return isset($v['persistence']) &&
|
||||
isset($v['persistence']['listener']) &&
|
||||
isset($v['persistence']['listener']['is_indexable_callback']);
|
||||
})
|
||||
->then(function ($v) {
|
||||
$v['indexable_callback'] = $v['persistence']['listener']['is_indexable_callback'];
|
||||
unset($v['persistence']['listener']['is_indexable_callback']);
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
->children()
|
||||
->scalarNode('index_analyzer')->end()
|
||||
->scalarNode('search_analyzer')->end()
|
||||
->scalarNode('indexable_callback')->end()
|
||||
->append($this->getPersistenceNode())
|
||||
->append($this->getSerializerNode())
|
||||
->end()
|
||||
|
@ -230,7 +244,7 @@ class Configuration implements ConfigurationInterface
|
|||
unset($v[$prop]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $v;
|
||||
})
|
||||
->end()
|
||||
|
@ -674,7 +688,6 @@ class Configuration implements ConfigurationInterface
|
|||
->treatTrueLike('fos_elastica.logger')
|
||||
->end()
|
||||
->scalarNode('service')->end()
|
||||
->variableNode('is_indexable_callback')->defaultNull()->end()
|
||||
->end()
|
||||
->end()
|
||||
->arrayNode('finder')
|
||||
|
|
|
@ -186,8 +186,11 @@ class FOSElasticaExtension extends Extension
|
|||
*/
|
||||
protected function loadTypes(array $types, ContainerBuilder $container, $indexName, $indexId, array $typePrototypeConfig)
|
||||
{
|
||||
$indexableCallbacks = array();
|
||||
|
||||
foreach ($types as $name => $type) {
|
||||
$type = self::deepArrayUnion($typePrototypeConfig, $type);
|
||||
$typeName = sprintf('%s/%s', $indexName, $name);
|
||||
$typeId = sprintf('%s.%s', $indexId, $name);
|
||||
$typeDefArgs = array($name);
|
||||
$typeDef = new Definition('%fos_elastica.type.class%', $typeDefArgs);
|
||||
|
@ -240,7 +243,6 @@ class FOSElasticaExtension extends Extension
|
|||
}
|
||||
if (isset($type['_parent'])) {
|
||||
$this->indexConfigs[$indexName]['config']['properties'][$name]['_parent'] = array('type' => $type['_parent']['type']);
|
||||
$typeName = sprintf('%s/%s', $indexName, $name);
|
||||
$this->typeFields[$typeName]['_parent'] = $type['_parent'];
|
||||
}
|
||||
if (isset($type['persistence'])) {
|
||||
|
@ -252,6 +254,9 @@ class FOSElasticaExtension extends Extension
|
|||
if (isset($type['search_analyzer'])) {
|
||||
$this->indexConfigs[$indexName]['config']['properties'][$name]['search_analyzer'] = $type['search_analyzer'];
|
||||
}
|
||||
if (isset($type['indexable_callback'])) {
|
||||
$indexableCallbacks[$typeName] = $type['indexable_callback'];
|
||||
}
|
||||
if (isset($type['index'])) {
|
||||
$this->indexConfigs[$indexName]['config']['properties'][$name]['index'] = $type['index'];
|
||||
}
|
||||
|
@ -271,6 +276,9 @@ class FOSElasticaExtension extends Extension
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
$indexable = $container->getDefinition('fos_elastica.indexable');
|
||||
$indexable->replaceArgument(0, $indexableCallbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -431,8 +439,7 @@ class FOSElasticaExtension extends Extension
|
|||
$listenerId = sprintf('fos_elastica.listener.%s.%s', $indexName, $typeName);
|
||||
$listenerDef = new DefinitionDecorator($abstractListenerId);
|
||||
$listenerDef->replaceArgument(0, new Reference($objectPersisterId));
|
||||
$listenerDef->replaceArgument(1, $typeConfig['model']);
|
||||
$listenerDef->replaceArgument(2, $this->getDoctrineEvents($typeConfig));
|
||||
$listenerDef->replaceArgument(1, $this->getDoctrineEvents($typeConfig));
|
||||
$listenerDef->replaceArgument(3, $typeConfig['identifier']);
|
||||
if ($typeConfig['listener']['logger']) {
|
||||
$listenerDef->replaceArgument(4, new Reference($typeConfig['listener']['logger']));
|
||||
|
@ -442,18 +449,7 @@ class FOSElasticaExtension extends Extension
|
|||
case 'orm': $listenerDef->addTag('doctrine.event_subscriber'); break;
|
||||
case 'mongodb': $listenerDef->addTag('doctrine_mongodb.odm.event_subscriber'); break;
|
||||
}
|
||||
if (isset($typeConfig['listener']['is_indexable_callback'])) {
|
||||
$callback = $typeConfig['listener']['is_indexable_callback'];
|
||||
|
||||
if (is_array($callback)) {
|
||||
list($class) = $callback + array(null);
|
||||
if (is_string($class) && !class_exists($class)) {
|
||||
$callback[0] = new Reference($class);
|
||||
}
|
||||
}
|
||||
|
||||
$listenerDef->addMethodCall('setIsIndexableCallback', array($callback));
|
||||
}
|
||||
$container->setDefinition($listenerId, $listenerDef);
|
||||
|
||||
return $listenerId;
|
||||
|
|
|
@ -2,15 +2,13 @@
|
|||
|
||||
namespace FOS\ElasticaBundle\Doctrine;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
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;
|
||||
use FOS\ElasticaBundle\Provider\IndexableInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* Automatically update ElasticSearch based on changes to the Doctrine source
|
||||
|
@ -25,13 +23,6 @@ class Listener implements EventSubscriber
|
|||
*/
|
||||
protected $objectPersister;
|
||||
|
||||
/**
|
||||
* Class of the domain model
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $objectClass;
|
||||
|
||||
/**
|
||||
* List of subscribed events
|
||||
*
|
||||
|
@ -46,13 +37,6 @@ class Listener implements EventSubscriber
|
|||
*/
|
||||
protected $esIdentifierField;
|
||||
|
||||
/**
|
||||
* Callback for determining if an object should be indexed
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
protected $isIndexableCallback;
|
||||
|
||||
/**
|
||||
* Objects scheduled for insertion and replacement
|
||||
*/
|
||||
|
@ -64,13 +48,6 @@ class Listener implements EventSubscriber
|
|||
*/
|
||||
public $scheduledForDeletion = array();
|
||||
|
||||
/**
|
||||
* An instance of ExpressionLanguage
|
||||
*
|
||||
* @var ExpressionLanguage
|
||||
*/
|
||||
protected $expressionLanguage;
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance
|
||||
*
|
||||
|
@ -78,26 +55,36 @@ class Listener implements EventSubscriber
|
|||
*/
|
||||
protected $propertyAccessor;
|
||||
|
||||
/**
|
||||
* @var \FOS\ElasticaBundle\Provider\IndexableInterface
|
||||
*/
|
||||
private $indexable;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ObjectPersisterInterface $objectPersister
|
||||
* @param string $objectClass
|
||||
* @param array $events
|
||||
* @param string $esIdentifierField
|
||||
* @param array $events
|
||||
* @param IndexableInterface $indexable
|
||||
* @param string $esIdentifierField
|
||||
* @param null $logger
|
||||
*/
|
||||
public function __construct(ObjectPersisterInterface $objectPersister, $objectClass, array $events, $esIdentifierField = 'id', $logger = null)
|
||||
{
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->objectClass = $objectClass;
|
||||
$this->events = $events;
|
||||
$this->esIdentifierField = $esIdentifierField;
|
||||
public function __construct(
|
||||
ObjectPersisterInterface $objectPersister,
|
||||
array $events,
|
||||
IndexableInterface $indexable,
|
||||
$esIdentifierField = 'id',
|
||||
$logger = null
|
||||
) {
|
||||
$this->esIdentifierField = $esIdentifierField;
|
||||
$this->events = $events;
|
||||
$this->indexable = $indexable;
|
||||
$this->objectPersister = $objectPersister;
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
|
||||
if ($logger) {
|
||||
$this->objectPersister->setLogger($logger);
|
||||
}
|
||||
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,82 +95,6 @@ class Listener implements EventSubscriber
|
|||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides unified method for retrieving a doctrine object from an EventArgs instance
|
||||
*
|
||||
|
@ -204,27 +115,11 @@ class Listener implements EventSubscriber
|
|||
throw new \RuntimeException('Unable to retrieve object from EventArgs.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 = $this->getDoctrineObject($eventArgs);
|
||||
|
||||
if ($entity instanceof $this->objectClass && $this->isObjectIndexable($entity)) {
|
||||
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForInsertion[] = $entity;
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +128,7 @@ class Listener implements EventSubscriber
|
|||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
if ($this->isObjectIndexable($entity)) {
|
||||
$this->scheduledForUpdate[] = $entity;
|
||||
} else {
|
||||
|
@ -251,7 +146,7 @@ class Listener implements EventSubscriber
|
|||
{
|
||||
$entity = $this->getDoctrineObject($eventArgs);
|
||||
|
||||
if ($entity instanceof $this->objectClass) {
|
||||
if ($this->objectPersister->handlesObject($entity)) {
|
||||
$this->scheduleForDeletion($entity);
|
||||
}
|
||||
}
|
||||
|
@ -305,4 +200,19 @@ class Listener implements EventSubscriber
|
|||
$this->scheduledForDeletion[] = $identifierValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the object is indexable or not.
|
||||
*
|
||||
* @param object $object
|
||||
* @return bool
|
||||
*/
|
||||
private function isObjectIndexable($object)
|
||||
{
|
||||
return $this->indexable->isObjectIndexable(
|
||||
$this->objectPersister->getType()->getIndex()->getName(),
|
||||
$this->objectPersister->getType()->getName(),
|
||||
$object
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,27 @@ class ObjectPersister implements ObjectPersisterInterface
|
|||
$this->fields = $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Temporary method that will be removed.
|
||||
*
|
||||
* @return Type
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the ObjectPersister handles a given object.
|
||||
*
|
||||
* @param object $object
|
||||
* @return bool
|
||||
*/
|
||||
public function handlesObject($object)
|
||||
{
|
||||
return $object instanceof $this->objectClass;
|
||||
}
|
||||
|
||||
public function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
|
|
174
Provider/Indexable.php
Normal file
174
Provider/Indexable.php
Normal file
|
@ -0,0 +1,174 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) FriendsOfSymfony <https://github.com/FriendsOfSymfony/FOSElasticaBundle/graphs/contributors>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
use Symfony\Component\ExpressionLanguage\Expression;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
use Symfony\Component\ExpressionLanguage\SyntaxError;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
class Indexable implements IndexableInterface
|
||||
{
|
||||
/**
|
||||
* An array of raw configured callbacks for all types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $callbacks = array();
|
||||
|
||||
/**
|
||||
* An instance of ExpressionLanguage
|
||||
*
|
||||
* @var ExpressionLanguage
|
||||
*/
|
||||
private $expressionLanguage;
|
||||
|
||||
/**
|
||||
* An array of initialised callbacks.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $initialisedCallbacks = array();
|
||||
|
||||
/**
|
||||
* PropertyAccessor instance
|
||||
*
|
||||
* @var PropertyAccessorInterface
|
||||
*/
|
||||
private $propertyAccessor;
|
||||
|
||||
/**
|
||||
* @param array $callbacks
|
||||
*/
|
||||
public function __construct(array $callbacks)
|
||||
{
|
||||
$this->callbacks = $callbacks;
|
||||
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the object is indexable with respect to the callback.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param mixed $object
|
||||
* @return bool
|
||||
*/
|
||||
public function isObjectIndexable($indexName, $typeName, $object)
|
||||
{
|
||||
$type = sprintf('%s/%s', $indexName, $typeName);
|
||||
$callback = $this->getCallback($type, $object);
|
||||
if (!$callback) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($callback instanceof Expression) {
|
||||
return $this->getExpressionLanguage()->evaluate($callback, array(
|
||||
'object' => $object,
|
||||
$this->getExpressionVar($object) => $object
|
||||
));
|
||||
}
|
||||
|
||||
return is_string($callback)
|
||||
? call_user_func(array($object, $callback))
|
||||
: call_user_func($callback, $object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and initialises a callback.
|
||||
*
|
||||
* @param string $type
|
||||
* @param object $object
|
||||
* @return mixed
|
||||
*/
|
||||
private function buildCallback($type, $object)
|
||||
{
|
||||
if (!array_key_exists($type, $this->callbacks)) {
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not configured', $type));
|
||||
}
|
||||
|
||||
$callback = $this->callbacks[$type];
|
||||
|
||||
if (is_callable($callback) or is_callable(array($object, $callback))) {
|
||||
return $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 \InvalidArgumentException(sprintf('Callback for type "%s", "%s::%s()", is not callable.', $type, $class, $method));
|
||||
}
|
||||
}
|
||||
|
||||
if (is_string($callback) && $expression = $this->getExpressionLanguage()) {
|
||||
$callback = new Expression($callback);
|
||||
|
||||
try {
|
||||
$expression->compile($callback, array('object', $this->getExpressionVar($object)));
|
||||
|
||||
return $callback;
|
||||
} catch (SyntaxError $e) {
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is an invalid expression', $type), $e->getCode(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives a cached callback, or creates a new callback if one is not found.
|
||||
*
|
||||
* @param string $type
|
||||
* @param object $object
|
||||
* @return mixed
|
||||
*/
|
||||
private function getCallback($type, $object)
|
||||
{
|
||||
if (!array_key_exists($type, $this->initialisedCallbacks)) {
|
||||
$this->initialisedCallbacks[$type] = $this->buildCallback($type, $object);
|
||||
}
|
||||
|
||||
return $this->initialisedCallbacks[$type];
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $object
|
||||
* @return string
|
||||
*/
|
||||
private function getExpressionVar($object = null)
|
||||
{
|
||||
$ref = new \ReflectionClass($object);
|
||||
|
||||
return strtolower($ref->getShortName());
|
||||
}
|
||||
}
|
25
Provider/IndexableInterface.php
Normal file
25
Provider/IndexableInterface.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Provider;
|
||||
|
||||
interface IndexableInterface
|
||||
{
|
||||
/**
|
||||
* Checks if an object passed should be indexable or not.
|
||||
*
|
||||
* @param string $indexName
|
||||
* @param string $typeName
|
||||
* @param mixed $object
|
||||
* @return bool
|
||||
*/
|
||||
public function isObjectIndexable($indexName, $typeName, $object);
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
<parameter key="fos_elastica.index.class">FOS\ElasticaBundle\DynamicIndex</parameter>
|
||||
<parameter key="fos_elastica.type.class">Elastica\Type</parameter>
|
||||
<parameter key="fos_elastica.index_manager.class">FOS\ElasticaBundle\IndexManager</parameter>
|
||||
<parameter key="fos_elastica.indexable.class">FOS\ElasticaBundle\Provider\Indexable</parameter>
|
||||
<parameter key="fos_elastica.resetter.class">FOS\ElasticaBundle\Resetter</parameter>
|
||||
<parameter key="fos_elastica.finder.class">FOS\ElasticaBundle\Finder\TransformedFinder</parameter>
|
||||
<parameter key="fos_elastica.logger.class">FOS\ElasticaBundle\Logger\ElasticaLogger</parameter>
|
||||
|
@ -44,6 +45,10 @@
|
|||
<argument /> <!-- index configs -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.indexable" class="%fos_elastica.indexable.class%">
|
||||
<argument type="collection" /> <!-- array of indexable callbacks keyed by type name -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.object_persister" class="%fos_elastica.object_persister.class%" abstract="true">
|
||||
<argument /> <!-- type -->
|
||||
<argument /> <!-- model to elastica transformer -->
|
||||
|
|
|
@ -15,9 +15,10 @@
|
|||
|
||||
<service id="fos_elastica.listener.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\Listener" public="false">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument/> <!-- identifier -->
|
||||
<argument /> <!-- logger -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.mongodb" class="FOS\ElasticaBundle\Doctrine\MongoDB\ElasticaToModelTransformer" public="false">
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
|
||||
<service id="fos_elastica.listener.prototype.orm" class="FOS\ElasticaBundle\Doctrine\Listener" public="false">
|
||||
<argument /> <!-- object persister -->
|
||||
<argument /> <!-- model -->
|
||||
<argument type="collection" /> <!-- events -->
|
||||
<argument/> <!-- identifier -->
|
||||
<argument /> <!-- check method -->
|
||||
<argument type="service" id="fos_elastica.indexable" />
|
||||
<argument /> <!-- identifier -->
|
||||
<argument /> <!-- logger -->
|
||||
</service>
|
||||
|
||||
<service id="fos_elastica.elastica_to_model_transformer.prototype.orm" class="FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer" public="false">
|
||||
|
|
|
@ -11,12 +11,12 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
public function testObjectInsertedOnPersist()
|
||||
{
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener = $this->createListener($persister, array(), $indexable);
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForInsertion));
|
||||
|
@ -28,18 +28,14 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsIndexableCallbacks
|
||||
*/
|
||||
public function testNonIndexableObjectNotInsertedOnPersist($isIndexableCallback)
|
||||
public function testNonIndexableObjectNotInsertedOnPersist()
|
||||
{
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1, false);
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, false);
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
$listener = $this->createListener($persister, array(), $indexable);
|
||||
$listener->postPersist($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForInsertion);
|
||||
|
@ -54,12 +50,12 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
public function testObjectReplacedOnUpdate()
|
||||
{
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $this->getMockObjectManager());
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, true);
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener = $this->createListener($persister, array(), $indexable);
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEquals($entity, current($listener->scheduledForUpdate));
|
||||
|
@ -73,17 +69,15 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideIsIndexableCallbacks
|
||||
*/
|
||||
public function testNonIndexableObjectRemovedOnUpdate($isIndexableCallback)
|
||||
public function testNonIndexableObjectRemovedOnUpdate()
|
||||
{
|
||||
$classMetadata = $this->getMockClassMetadata();
|
||||
$objectManager = $this->getMockObjectManager();
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1, false);
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $objectManager);
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity, false);
|
||||
|
||||
$objectManager->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
|
@ -95,8 +89,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
$listener = $this->createListener($persister, array(), $indexable);
|
||||
$listener->postUpdate($eventArgs);
|
||||
|
||||
$this->assertEmpty($listener->scheduledForUpdate);
|
||||
|
@ -115,10 +108,11 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$classMetadata = $this->getMockClassMetadata();
|
||||
$objectManager = $this->getMockObjectManager();
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $objectManager);
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity);
|
||||
|
||||
$objectManager->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
|
@ -130,7 +124,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'id')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array());
|
||||
$listener = $this->createListener($persister, array(), $indexable);
|
||||
$listener->preRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity->getId(), current($listener->scheduledForDeletion));
|
||||
|
@ -146,11 +140,12 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
{
|
||||
$classMetadata = $this->getMockClassMetadata();
|
||||
$objectManager = $this->getMockObjectManager();
|
||||
$persister = $this->getMockPersister();
|
||||
|
||||
$entity = new Listener\Entity(1);
|
||||
$entity->identifier = 'foo';
|
||||
$persister = $this->getMockPersister($entity, 'index', 'type');
|
||||
$eventArgs = $this->createLifecycleEventArgs($entity, $objectManager);
|
||||
$indexable = $this->getMockIndexable('index', 'type', $entity);
|
||||
|
||||
$objectManager->expects($this->any())
|
||||
->method('getClassMetadata')
|
||||
|
@ -162,7 +157,7 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->with($entity, 'identifier')
|
||||
->will($this->returnValue($entity->getId()));
|
||||
|
||||
$listener = $this->createListener($persister, get_class($entity), array(), 'identifier');
|
||||
$listener = $this->createListener($persister, array(), $indexable, 'identifier');
|
||||
$listener->preRemove($eventArgs);
|
||||
|
||||
$this->assertEquals($entity->identifier, current($listener->scheduledForDeletion));
|
||||
|
@ -174,36 +169,6 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
$listener->postFlush($eventArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidIsIndexableCallbacks
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testInvalidIsIndexableCallbacks($isIndexableCallback)
|
||||
{
|
||||
$listener = $this->createListener($this->getMockPersister(), 'FOS\ElasticaBundle\Tests\Doctrine\Listener\Entity', array());
|
||||
$listener->setIsIndexableCallback($isIndexableCallback);
|
||||
}
|
||||
|
||||
public function provideInvalidIsIndexableCallbacks()
|
||||
{
|
||||
return array(
|
||||
array('nonexistentEntityMethod'),
|
||||
array(array(new Listener\IndexableDecider(), 'internalMethod')),
|
||||
array(42),
|
||||
array('entity.getIsIndexable() && nonexistentEntityFunction()'),
|
||||
);
|
||||
}
|
||||
|
||||
public function provideIsIndexableCallbacks()
|
||||
{
|
||||
return array(
|
||||
array('getIsIndexable'),
|
||||
array(array(new Listener\IndexableDecider(), 'isIndexable')),
|
||||
array(function(Listener\Entity $entity) { return $entity->getIsIndexable(); }),
|
||||
array('entity.getIsIndexable()')
|
||||
);
|
||||
}
|
||||
|
||||
abstract protected function getLifecycleEventArgsClass();
|
||||
|
||||
abstract protected function getListenerClass();
|
||||
|
@ -240,9 +205,48 @@ abstract class ListenerTest extends \PHPUnit_Framework_TestCase
|
|||
->getMock();
|
||||
}
|
||||
|
||||
private function getMockPersister()
|
||||
private function getMockPersister($object, $indexName, $typeName)
|
||||
{
|
||||
return $this->getMock('FOS\ElasticaBundle\Persister\ObjectPersisterInterface');
|
||||
$mock = $this->getMockBuilder('FOS\ElasticaBundle\Persister\ObjectPersister')
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('handlesObject')
|
||||
->with($object)
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$index = $this->getMockBuilder('Elastica\Index')->disableOriginalConstructor()->getMock();
|
||||
$index->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($indexName));
|
||||
$type = $this->getMockBuilder('Elastica\Type')->disableOriginalConstructor()->getMock();
|
||||
$type->expects($this->any())
|
||||
->method('getName')
|
||||
->will($this->returnValue($typeName));
|
||||
$type->expects($this->any())
|
||||
->method('getIndex')
|
||||
->will($this->returnValue($index));
|
||||
|
||||
$mock->expects($this->any())
|
||||
->method('getType')
|
||||
->will($this->returnValue($type));
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
private function getMockIndexable($indexName, $typeName, $object, $return = null)
|
||||
{
|
||||
$mock = $this->getMock('FOS\ElasticaBundle\Provider\IndexableInterface');
|
||||
|
||||
if (null !== $return) {
|
||||
$mock->expects($this->once())
|
||||
->method('isObjectIndexable')
|
||||
->with($indexName, $typeName, $object)
|
||||
->will($this->returnValue($return));
|
||||
}
|
||||
|
||||
return $mock;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -251,33 +255,15 @@ namespace FOS\ElasticaBundle\Tests\Doctrine\Listener;
|
|||
class Entity
|
||||
{
|
||||
private $id;
|
||||
private $isIndexable;
|
||||
|
||||
public function __construct($id, $isIndexable = true)
|
||||
public function __construct($id)
|
||||
{
|
||||
$this->id = $id;
|
||||
$this->isIndexable = $isIndexable;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getIsIndexable()
|
||||
{
|
||||
return $this->isIndexable;
|
||||
}
|
||||
}
|
||||
|
||||
class IndexableDecider
|
||||
{
|
||||
public function isIndexable(Entity $entity)
|
||||
{
|
||||
return $entity->getIsIndexable();
|
||||
}
|
||||
|
||||
protected function internalMethod()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
51
Tests/Functional/IndexableCallbackTest.php
Normal file
51
Tests/Functional/IndexableCallbackTest.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Tim Nagel <tim@nagel.com.au>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional;
|
||||
|
||||
/**
|
||||
* @group functional
|
||||
*/
|
||||
class IndexableCallbackTest extends WebTestCase
|
||||
{
|
||||
/**
|
||||
* 2 reasons for this test:
|
||||
*
|
||||
* 1) To test that the configuration rename from is_indexable_callback under the listener
|
||||
* key is respected, and
|
||||
* 2) To test the Extension's set up of the Indexable service.
|
||||
*/
|
||||
public function testIndexableCallback()
|
||||
{
|
||||
$client = $this->createClient(array('test_case' => 'ORM'));
|
||||
|
||||
/** @var \FOS\ElasticaBundle\Provider\Indexable $in */
|
||||
$in = $client->getContainer()->get('fos_elastica.indexable');
|
||||
|
||||
$this->assertTrue($in->isObjectIndexable('index', 'type', new TypeObj()));
|
||||
$this->assertFalse($in->isObjectIndexable('index', 'type2', new TypeObj()));
|
||||
$this->assertFalse($in->isObjectIndexable('index', 'type3', new TypeObj()));
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->deleteTmpDir('ORM');
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
parent::tearDown();
|
||||
|
||||
$this->deleteTmpDir('ORM');
|
||||
}
|
||||
}
|
25
Tests/Functional/TypeObj.php
Normal file
25
Tests/Functional/TypeObj.php
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Functional;
|
||||
|
||||
class TypeObj
|
||||
{
|
||||
public function isIndexable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isntIndexable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -50,4 +50,4 @@ fos_elastica:
|
|||
_parent:
|
||||
type: "parent"
|
||||
property: "parent"
|
||||
identifier: "id"
|
||||
identifier: "id"
|
||||
|
|
11
Tests/Functional/app/ORM/bundles.php
Normal file
11
Tests/Functional/app/ORM/bundles.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
|
||||
use FOS\ElasticaBundle\FOSElasticaBundle;
|
||||
use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
|
||||
|
||||
return array(
|
||||
new FrameworkBundle(),
|
||||
new FOSElasticaBundle(),
|
||||
new DoctrineBundle()
|
||||
);
|
44
Tests/Functional/app/ORM/config.yml
Normal file
44
Tests/Functional/app/ORM/config.yml
Normal file
|
@ -0,0 +1,44 @@
|
|||
imports:
|
||||
- { resource: ./../config/config.yml }
|
||||
|
||||
doctrine:
|
||||
dbal:
|
||||
path: %kernel.cache_dir%/db.sqlite
|
||||
charset: UTF8
|
||||
orm:
|
||||
auto_generate_proxy_classes: false
|
||||
auto_mapping: false
|
||||
|
||||
fos_elastica:
|
||||
clients:
|
||||
default:
|
||||
url: http://localhost:9200
|
||||
indexes:
|
||||
index:
|
||||
index_name: foselastica_test_%kernel.environment%
|
||||
types:
|
||||
type:
|
||||
properties:
|
||||
field1: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
listener:
|
||||
is_indexable_callback: 'object.isIndexable() && !object.isntIndexable()'
|
||||
type2:
|
||||
properties:
|
||||
field1: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
listener:
|
||||
is_indexable_callback: 'object.isntIndexable()'
|
||||
type3:
|
||||
properties:
|
||||
field1: ~
|
||||
persistence:
|
||||
driver: orm
|
||||
model: FOS\ElasticaBundle\Tests\Functional\TypeObj
|
||||
listener:
|
||||
is_indexable_callback: 'isntIndexable'
|
||||
|
91
Tests/Provider/IndexableTest.php
Normal file
91
Tests/Provider/IndexableTest.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the FOSElasticaBundle project.
|
||||
*
|
||||
* (c) Infinite Networks Pty Ltd <http://www.infinite.net.au>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace FOS\ElasticaBundle\Tests\Provider;
|
||||
|
||||
use FOS\ElasticaBundle\Provider\Indexable;
|
||||
|
||||
class IndexableTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideIsIndexableCallbacks
|
||||
*/
|
||||
public function testValidIndexableCallbacks($callback, $return)
|
||||
{
|
||||
$indexable = new Indexable(array(
|
||||
'index/type' => $callback
|
||||
));
|
||||
$index = $indexable->isObjectIndexable('index', 'type', new Entity);
|
||||
|
||||
$this->assertEquals($return, $index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidIsIndexableCallbacks
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testInvalidIsIndexableCallbacks($callback)
|
||||
{
|
||||
$indexable = new Indexable(array(
|
||||
'index/type' => $callback
|
||||
));
|
||||
$indexable->isObjectIndexable('index', 'type', new Entity);
|
||||
}
|
||||
|
||||
public function provideInvalidIsIndexableCallbacks()
|
||||
{
|
||||
return array(
|
||||
array('nonexistentEntityMethod'),
|
||||
array(array(new IndexableDecider(), 'internalMethod')),
|
||||
array(42),
|
||||
array('entity.getIsIndexable() && nonexistentEntityFunction()'),
|
||||
);
|
||||
}
|
||||
|
||||
public function provideIsIndexableCallbacks()
|
||||
{
|
||||
return array(
|
||||
array('isIndexable', false),
|
||||
array(array(new IndexableDecider(), 'isIndexable'), true),
|
||||
array(function(Entity $entity) { return $entity->maybeIndex(); }, true),
|
||||
array('entity.maybeIndex()', true),
|
||||
array('!object.isIndexable() && entity.property == "abc"', true),
|
||||
array('entity.property != "abc"', false),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Entity
|
||||
{
|
||||
public $property = 'abc';
|
||||
|
||||
public function isIndexable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function maybeIndex()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class IndexableDecider
|
||||
{
|
||||
public function isIndexable(Entity $entity)
|
||||
{
|
||||
return !$entity->isIndexable();
|
||||
}
|
||||
|
||||
protected function internalMethod()
|
||||
{
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
},
|
||||
"require-dev":{
|
||||
"doctrine/orm": "~2.2",
|
||||
"doctrine/doctrine-bundle": "~1.2@beta",
|
||||
"doctrine/mongodb-odm": "1.0.*@dev",
|
||||
"propel/propel1": "1.6.*",
|
||||
"pagerfanta/pagerfanta": "1.0.*@dev",
|
||||
|
|
Loading…
Reference in a new issue