Add Symfony ExpressionLanguage support for indexable callback

This commit is contained in:
Pierre du Plessis 2013-11-04 00:09:21 +02:00
parent 28641427d5
commit 97c98a0243
4 changed files with 71 additions and 3 deletions

View file

@ -6,6 +6,9 @@ use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\ObjectManager;
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
{
@ -51,6 +54,13 @@ abstract class AbstractListener implements EventSubscriber
*/
private $scheduledForRemoval = array();
/**
* An instance of ExpressionLanguage
*
* @var ExpressionLanguage
*/
protected $expressionLanguage;
/**
* Constructor.
*
@ -89,8 +99,21 @@ abstract class AbstractListener implements EventSubscriber
public function setIsIndexableCallback($callback)
{
if (is_string($callback)) {
if (!is_callable(array($this->objectClass, $callback))) {
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $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)) {
@ -98,6 +121,7 @@ abstract class AbstractListener implements EventSubscriber
if (is_object($class)) {
$class = get_class($class);
}
if ($class && $method) {
throw new \RuntimeException(sprintf('Indexable callback %s::%s() is not callable.', $class, $method));
}
@ -120,6 +144,10 @@ abstract class AbstractListener implements EventSubscriber
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);
@ -155,4 +183,33 @@ abstract class AbstractListener implements EventSubscriber
unset($this->scheduledForRemoval[$objectHash]);
}
}
/**
* @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;
}
}

View file

@ -630,6 +630,13 @@ In this case, the callback_class will be the `isIndexable()` method on the speci
service and the object being considered for indexing will be passed as the only
argument. This allows you to do more complex validation (e.g. ACL checks).
If you have the [Symfony ExpressionLanguage](https://github.com/symfony/expression-language) component installed, you can use expressions
to evaluate the callback:
persistence:
listener:
is_indexable_callback: "user.isActive() && user.hasRole('ROLE_USER')"
As you might expect, new entities will only be indexed if the callback_class returns
`true`. Additionally, modified entities will be updated or removed from the
index depending on whether the callback_class returns `true` or `false`, respectively.

View file

@ -164,6 +164,7 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
array('nonexistentEntityMethod'),
array(array(new Listener\IndexableDecider(), 'internalMethod')),
array(42),
array('entity.getIsIndexable() && nonexistentEntityFunction()'),
);
}
@ -173,6 +174,7 @@ abstract class AbstractListenerTest extends \PHPUnit_Framework_TestCase
array('getIsIndexable'),
array(array(new Listener\IndexableDecider(), 'isIndexable')),
array(function(Listener\Entity $entity) { return $entity->getIsIndexable(); }),
array('entity.getIsIndexable()')
);
}

View file

@ -24,14 +24,16 @@
"doctrine/mongodb-odm": "1.0.*@dev",
"propel/propel1": "1.6.*",
"pagerfanta/pagerfanta": "1.0.*@dev",
"knplabs/knp-components": "1.2.*"
"knplabs/knp-components": "1.2.*",
"symfony/expression-language" : "2.4.*@dev"
},
"suggest": {
"doctrine/orm": ">=2.2,<2.5-dev",
"doctrine/mongodb-odm": "1.0.*@dev",
"propel/propel1": "1.6.*",
"pagerfanta/pagerfanta": "1.0.*@dev",
"knplabs/knp-components": "1.2.*"
"knplabs/knp-components": "1.2.*",
"symfony/expression-language" : "2.4.*@dev"
},
"autoload": {
"psr-0": { "FOS\\ElasticaBundle": "" }