Indexable service improvements
This commit is contained in:
parent
9cf0117c71
commit
72a9dfa267
|
@ -44,3 +44,4 @@ https://github.com/FriendsOfSymfony/FOSElasticaBundle/compare/v3.0.4...v3.1.0
|
||||||
* New events `PRE_INDEX_RESET`, `POST_INDEX_RESET`, `PRE_TYPE_RESET` and
|
* New events `PRE_INDEX_RESET`, `POST_INDEX_RESET`, `PRE_TYPE_RESET` and
|
||||||
`POST_TYPE_RESET` are run before and after operations that will reset an
|
`POST_TYPE_RESET` are run before and after operations that will reset an
|
||||||
index. #744
|
index. #744
|
||||||
|
* Added indexable callback support for the __invoke method of a service. #823
|
||||||
|
|
|
@ -110,7 +110,7 @@ class AliasProcessor
|
||||||
* Cleans up an index when we encounter a failure to rename the alias.
|
* Cleans up an index when we encounter a failure to rename the alias.
|
||||||
*
|
*
|
||||||
* @param Client $client
|
* @param Client $client
|
||||||
* @param $indexName
|
* @param string $indexName
|
||||||
* @param \Exception $renameAliasException
|
* @param \Exception $renameAliasException
|
||||||
*/
|
*/
|
||||||
private function cleanupRenameFailure(Client $client, $indexName, \Exception $renameAliasException)
|
private function cleanupRenameFailure(Client $client, $indexName, \Exception $renameAliasException)
|
||||||
|
|
|
@ -55,6 +55,7 @@ class Indexable implements IndexableInterface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array $callbacks
|
* @param array $callbacks
|
||||||
|
* @param ContainerInterface $container
|
||||||
*/
|
*/
|
||||||
public function __construct(array $callbacks, ContainerInterface $container)
|
public function __construct(array $callbacks, ContainerInterface $container)
|
||||||
{
|
{
|
||||||
|
@ -112,39 +113,48 @@ class Indexable implements IndexableInterface
|
||||||
return $callback;
|
return $callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($callback)) {
|
if (is_array($callback) && !is_object($callback[0])) {
|
||||||
list($class, $method) = $callback + array(null, null);
|
return $this->processArrayToCallback($type, $callback);
|
||||||
|
|
||||||
if (is_object($class)) {
|
|
||||||
$class = get_class($class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strpos($class, '@') === 0) {
|
if (is_string($callback)) {
|
||||||
$service = $this->container->get(substr($class, 1));
|
return $this->buildExpressionCallback($type, $object, $callback);
|
||||||
|
|
||||||
return array($service, $method);
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
throw new \InvalidArgumentException(sprintf('Callback for type "%s" is not a valid callback.', $type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a string expression into an Expression.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param mixed $object
|
||||||
|
* @param string $callback
|
||||||
|
*
|
||||||
|
* @return Expression
|
||||||
|
*/
|
||||||
|
private function buildExpressionCallback($type, $object, $callback)
|
||||||
|
{
|
||||||
|
$expression = $this->getExpressionLanguage();
|
||||||
|
if (!$expression) {
|
||||||
|
throw new \RuntimeException('Unable to process an expression without the ExpressionLanguage component.');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$callback = new Expression($callback);
|
||||||
|
$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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retreives a cached callback, or creates a new callback if one is not found.
|
* Retreives a cached callback, or creates a new callback if one is not found.
|
||||||
*
|
*
|
||||||
|
@ -163,15 +173,13 @@ class Indexable implements IndexableInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return bool|ExpressionLanguage
|
* Returns the ExpressionLanguage class if it is available.
|
||||||
|
*
|
||||||
|
* @return ExpressionLanguage|null
|
||||||
*/
|
*/
|
||||||
private function getExpressionLanguage()
|
private function getExpressionLanguage()
|
||||||
{
|
{
|
||||||
if (null === $this->expressionLanguage) {
|
if (null === $this->expressionLanguage && class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
||||||
if (!class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->expressionLanguage = new ExpressionLanguage();
|
$this->expressionLanguage = new ExpressionLanguage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,14 +187,54 @@ class Indexable implements IndexableInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Returns the variable name to be used to access the object when using the ExpressionLanguage
|
||||||
|
* component to parse and evaluate an expression.
|
||||||
|
*
|
||||||
* @param mixed $object
|
* @param mixed $object
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function getExpressionVar($object = null)
|
private function getExpressionVar($object = null)
|
||||||
{
|
{
|
||||||
|
if (!is_object($object)) {
|
||||||
|
return 'object';
|
||||||
|
}
|
||||||
|
|
||||||
$ref = new \ReflectionClass($object);
|
$ref = new \ReflectionClass($object);
|
||||||
|
|
||||||
return strtolower($ref->getShortName());
|
return strtolower($ref->getShortName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes an array into a callback. Replaces the first element with a service if
|
||||||
|
* it begins with an @.
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @param array $callback
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
private function processArrayToCallback($type, array $callback)
|
||||||
|
{
|
||||||
|
list($class, $method) = $callback + array(null, '__invoke');
|
||||||
|
|
||||||
|
if (strpos($class, '@') === 0) {
|
||||||
|
$service = $this->container->get(substr($class, 1));
|
||||||
|
$callback = array($service, $method);
|
||||||
|
|
||||||
|
if (!is_callable($callback)) {
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'Method "%s" on service "%s" is not callable.',
|
||||||
|
$method,
|
||||||
|
substr($class, 1)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(sprintf(
|
||||||
|
'Unable to parse callback array for type "%s"',
|
||||||
|
$type
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,13 +201,18 @@ index enabled users.
|
||||||
The callback option supports multiple approaches:
|
The callback option supports multiple approaches:
|
||||||
|
|
||||||
* A method on the object itself provided as a string. `enabled` will call
|
* A method on the object itself provided as a string. `enabled` will call
|
||||||
`Object->enabled()`
|
`Object->enabled()`. Note that this does not support chaining methods with dot notation
|
||||||
|
like property paths. To achieve something similar use the ExpressionLanguage option
|
||||||
|
below.
|
||||||
* An array of a service id and a method which will be called with the object as the first
|
* An array of a service id and a method which will be called with the object as the first
|
||||||
and only argument. `[ @my_custom_service, 'userIndexable' ]` will call the userIndexable
|
and only argument. `[ @my_custom_service, 'userIndexable' ]` will call the userIndexable
|
||||||
method on a service defined as my_custom_service.
|
method on a service defined as my_custom_service.
|
||||||
* An array of a class and a static method to call on that class which will be called with
|
* An array of a class and a static method to call on that class which will be called with
|
||||||
the object as the only argument. `[ 'Acme\DemoBundle\IndexableChecker', 'isIndexable' ]`
|
the object as the only argument. `[ 'Acme\DemoBundle\IndexableChecker', 'isIndexable' ]`
|
||||||
will call Acme\DemoBundle\IndexableChecker::isIndexable($object)
|
will call Acme\DemoBundle\IndexableChecker::isIndexable($object)
|
||||||
|
* A single element array with a service id can be used if the service has an __invoke
|
||||||
|
method. Such an invoke method must accept a single parameter for the object to be indexed.
|
||||||
|
`[ @my_custom_invokable_service ]`
|
||||||
* If you have the ExpressionLanguage component installed, A valid ExpressionLanguage
|
* If you have the ExpressionLanguage component installed, A valid ExpressionLanguage
|
||||||
expression provided as a string. The object being indexed will be supplied as `object`
|
expression provided as a string. The object being indexed will be supplied as `object`
|
||||||
in the expression. `object.isEnabled() or object.shouldBeIndexedAnyway()`. For more
|
in the expression. `object.isEnabled() or object.shouldBeIndexedAnyway()`. For more
|
||||||
|
|
|
@ -55,6 +55,7 @@ class IndexableTest extends \PHPUnit_Framework_TestCase
|
||||||
{
|
{
|
||||||
return array(
|
return array(
|
||||||
array('nonexistentEntityMethod'),
|
array('nonexistentEntityMethod'),
|
||||||
|
array(array('@indexableService', 'internalMethod')),
|
||||||
array(array(new IndexableDecider(), 'internalMethod')),
|
array(array(new IndexableDecider(), 'internalMethod')),
|
||||||
array(42),
|
array(42),
|
||||||
array('entity.getIsIndexable() && nonexistentEntityFunction()'),
|
array('entity.getIsIndexable() && nonexistentEntityFunction()'),
|
||||||
|
@ -67,6 +68,7 @@ class IndexableTest extends \PHPUnit_Framework_TestCase
|
||||||
array('isIndexable', false),
|
array('isIndexable', false),
|
||||||
array(array(new IndexableDecider(), 'isIndexable'), true),
|
array(array(new IndexableDecider(), 'isIndexable'), true),
|
||||||
array(array('@indexableService', 'isIndexable'), true),
|
array(array('@indexableService', 'isIndexable'), true),
|
||||||
|
array(array('@indexableService'), true),
|
||||||
array(function (Entity $entity) { return $entity->maybeIndex(); }, true),
|
array(function (Entity $entity) { return $entity->maybeIndex(); }, true),
|
||||||
array('entity.maybeIndex()', true),
|
array('entity.maybeIndex()', true),
|
||||||
array('!object.isIndexable() && entity.property == "abc"', true),
|
array('!object.isIndexable() && entity.property == "abc"', true),
|
||||||
|
@ -111,4 +113,9 @@ class IndexableDecider
|
||||||
protected function internalMethod()
|
protected function internalMethod()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function __invoke($object)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue