211 lines
5.8 KiB
PHP
211 lines
5.8 KiB
PHP
<?php
|
|
|
|
namespace FOS\ElasticaBundle\Doctrine;
|
|
|
|
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
|
|
use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
|
|
use FOS\ElasticaBundle\Persister\ObjectPersister;
|
|
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
|
|
* data. One listener is generated for each Doctrine entity / ElasticSearch type.
|
|
*/
|
|
class Listener
|
|
{
|
|
/**
|
|
* Object persister
|
|
*
|
|
* @var ObjectPersister
|
|
*/
|
|
protected $objectPersister;
|
|
|
|
/**
|
|
* Configuration for the listener
|
|
*
|
|
* @var string
|
|
*/
|
|
private $config;
|
|
|
|
/**
|
|
* Objects scheduled for insertion.
|
|
*
|
|
* @var array
|
|
*/
|
|
public $scheduledForInsertion = array();
|
|
|
|
/**
|
|
* Objects scheduled to be updated or removed.
|
|
*
|
|
* @var array
|
|
*/
|
|
public $scheduledForUpdate = array();
|
|
|
|
/**
|
|
* IDs of objects scheduled for removal
|
|
*
|
|
* @var array
|
|
*/
|
|
public $scheduledForDeletion = array();
|
|
|
|
/**
|
|
* PropertyAccessor instance
|
|
*
|
|
* @var PropertyAccessorInterface
|
|
*/
|
|
protected $propertyAccessor;
|
|
|
|
/**
|
|
* @var IndexableInterface
|
|
*/
|
|
private $indexable;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param ObjectPersisterInterface $objectPersister
|
|
* @param IndexableInterface $indexable
|
|
* @param array $config
|
|
* @param null $logger
|
|
*/
|
|
public function __construct(
|
|
ObjectPersisterInterface $objectPersister,
|
|
IndexableInterface $indexable,
|
|
array $config = array(),
|
|
$logger = null
|
|
) {
|
|
$this->config = array_merge(array(
|
|
'identifier' => 'id',
|
|
), $config);
|
|
$this->indexable = $indexable;
|
|
$this->objectPersister = $objectPersister;
|
|
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
|
|
|
|
if ($logger) {
|
|
$this->objectPersister->setLogger($logger);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Looks for new objects that should be indexed.
|
|
*
|
|
* @param LifecycleEventArgs $eventArgs
|
|
*/
|
|
public function postPersist(LifecycleEventArgs $eventArgs)
|
|
{
|
|
$entity = $eventArgs->getObject();
|
|
|
|
if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
|
|
$this->scheduledForInsertion[] = $entity;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Looks for objects being updated that should be indexed or removed from the index.
|
|
*
|
|
* @param LifecycleEventArgs $eventArgs
|
|
*/
|
|
public function postUpdate(LifecycleEventArgs $eventArgs)
|
|
{
|
|
$entity = $eventArgs->getObject();
|
|
|
|
if ($this->objectPersister->handlesObject($entity)) {
|
|
if ($this->isObjectIndexable($entity)) {
|
|
$this->scheduledForUpdate[] = $entity;
|
|
} else {
|
|
// Delete if no longer indexable
|
|
$this->scheduleForDeletion($entity);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete objects preRemove instead of postRemove so that we have access to the id. Because this is called
|
|
* preRemove, first check that the entity is managed by Doctrine
|
|
*
|
|
* @param LifecycleEventArgs $eventArgs
|
|
*/
|
|
public function preRemove(LifecycleEventArgs $eventArgs)
|
|
{
|
|
$entity = $eventArgs->getObject();
|
|
|
|
if ($this->objectPersister->handlesObject($entity)) {
|
|
$this->scheduleForDeletion($entity);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Persist scheduled objects to ElasticSearch
|
|
* After persisting, clear the scheduled queue to prevent multiple data updates when using multiple flush calls
|
|
*/
|
|
private function persistScheduled()
|
|
{
|
|
if (count($this->scheduledForInsertion)) {
|
|
$this->objectPersister->insertMany($this->scheduledForInsertion);
|
|
$this->scheduledForInsertion = array();
|
|
}
|
|
if (count($this->scheduledForUpdate)) {
|
|
$this->objectPersister->replaceMany($this->scheduledForUpdate);
|
|
$this->scheduledForUpdate = array();
|
|
}
|
|
if (count($this->scheduledForDeletion)) {
|
|
$this->objectPersister->deleteManyByIdentifiers($this->scheduledForDeletion);
|
|
$this->scheduledForDeletion = array();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Iterate through scheduled actions before flushing to emulate 2.x behavior.
|
|
* Note that the ElasticSearch index will fall out of sync with the source
|
|
* data in the event of a crash during flush.
|
|
*
|
|
* This method is only called in legacy configurations of the listener.
|
|
*
|
|
* @deprecated This method should only be called in applications that depend
|
|
* on the behaviour that entities are indexed regardless of if a
|
|
* flush is successful.
|
|
*/
|
|
public function preFlush()
|
|
{
|
|
$this->persistScheduled();
|
|
}
|
|
|
|
/**
|
|
* Iterating through scheduled actions *after* flushing ensures that the
|
|
* ElasticSearch index will be affected only if the query is successful.
|
|
*/
|
|
public function postFlush()
|
|
{
|
|
$this->persistScheduled();
|
|
}
|
|
|
|
/**
|
|
* Record the specified identifier to delete. Do not need to entire object.
|
|
*
|
|
* @param object $object
|
|
*/
|
|
private function scheduleForDeletion($object)
|
|
{
|
|
if ($identifierValue = $this->propertyAccessor->getValue($object, $this->config['identifier'])) {
|
|
$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->config['indexName'],
|
|
$this->config['typeName'],
|
|
$object
|
|
);
|
|
}
|
|
}
|