vendor/friendsofsymfony/elastica-bundle/src/Doctrine/Listener.php line 105

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the FOSElasticaBundle package.
  4.  *
  5.  * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace FOS\ElasticaBundle\Doctrine;
  11. use Doctrine\Persistence\Event\LifecycleEventArgs;
  12. use FOS\ElasticaBundle\Persister\ObjectPersister;
  13. use FOS\ElasticaBundle\Persister\ObjectPersisterInterface;
  14. use FOS\ElasticaBundle\Provider\IndexableInterface;
  15. use Psr\Log\LoggerInterface;
  16. use Symfony\Component\PropertyAccess\PropertyAccess;
  17. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  18. /**
  19.  * Automatically update ElasticSearch based on changes to the Doctrine source
  20.  * data. One listener is generated for each Doctrine entity / ElasticSearch type.
  21.  */
  22. class Listener
  23. {
  24.     /**
  25.      * Objects scheduled for insertion.
  26.      *
  27.      * @var array
  28.      */
  29.     public $scheduledForInsertion = [];
  30.     /**
  31.      * Objects scheduled to be updated or removed.
  32.      *
  33.      * @var array
  34.      */
  35.     public $scheduledForUpdate = [];
  36.     /**
  37.      * IDs of objects scheduled for removal.
  38.      *
  39.      * @var array
  40.      */
  41.     public $scheduledForDeletion = [];
  42.     /**
  43.      * Object persister.
  44.      *
  45.      * @var ObjectPersisterInterface
  46.      */
  47.     protected $objectPersister;
  48.     /**
  49.      * PropertyAccessor instance.
  50.      *
  51.      * @var PropertyAccessorInterface
  52.      */
  53.     protected $propertyAccessor;
  54.     /**
  55.      * Configuration for the listener.
  56.      *
  57.      * @var array
  58.      */
  59.     private $config;
  60.     /**
  61.      * @var IndexableInterface
  62.      */
  63.     private $indexable;
  64.     /**
  65.      * Constructor.
  66.      *
  67.      * @param ObjectPersisterInterface $objectPersister
  68.      * @param IndexableInterface       $indexable
  69.      * @param array                    $config
  70.      * @param LoggerInterface          $logger
  71.      */
  72.     public function __construct(
  73.         ObjectPersisterInterface $objectPersister,
  74.         IndexableInterface $indexable,
  75.         array $config = [],
  76.         LoggerInterface $logger null
  77.     ) {
  78.         $this->config array_merge([
  79.             'identifier' => 'id',
  80.             'defer' => false,
  81.         ], $config);
  82.         $this->indexable $indexable;
  83.         $this->objectPersister $objectPersister;
  84.         $this->propertyAccessor PropertyAccess::createPropertyAccessor();
  85.         if ($logger && $this->objectPersister instanceof ObjectPersister) {
  86.             $this->objectPersister->setLogger($logger);
  87.         }
  88.     }
  89.     /**
  90.      * Handler for the "kernel.terminate" and "console.terminate" Symfony events.
  91.      * These event are subscribed to if the listener is configured to persist asynchronously.
  92.      */
  93.     public function onTerminate()
  94.     {
  95.         if ($this->config['defer']) {
  96.             $this->config['defer'] = false;
  97.             $this->persistScheduled();
  98.         }
  99.     }
  100.     /**
  101.      * Looks for new objects that should be indexed.
  102.      *
  103.      * @param LifecycleEventArgs $eventArgs
  104.      */
  105.     public function postPersist(LifecycleEventArgs $eventArgs)
  106.     {
  107.         $entity $eventArgs->getObject();
  108.         if ($this->objectPersister->handlesObject($entity) && $this->isObjectIndexable($entity)) {
  109.             $this->scheduledForInsertion[] = $entity;
  110.         }
  111.     }
  112.     /**
  113.      * Looks for objects being updated that should be indexed or removed from the index.
  114.      *
  115.      * @param LifecycleEventArgs $eventArgs
  116.      */
  117.     public function postUpdate(LifecycleEventArgs $eventArgs)
  118.     {
  119.         $entity $eventArgs->getObject();
  120.         if ($this->objectPersister->handlesObject($entity)) {
  121.             if ($this->isObjectIndexable($entity)) {
  122.                 $this->scheduledForUpdate[] = $entity;
  123.             } else {
  124.                 // Delete if no longer indexable
  125.                 $this->scheduleForDeletion($entity);
  126.             }
  127.         }
  128.     }
  129.     /**
  130.      * Delete objects preRemove instead of postRemove so that we have access to the id.  Because this is called
  131.      * preRemove, first check that the entity is managed by Doctrine.
  132.      *
  133.      * @param LifecycleEventArgs $eventArgs
  134.      */
  135.     public function preRemove(LifecycleEventArgs $eventArgs)
  136.     {
  137.         $entity $eventArgs->getObject();
  138.         if ($this->objectPersister->handlesObject($entity)) {
  139.             $this->scheduleForDeletion($entity);
  140.         }
  141.     }
  142.     /**
  143.      * Iterate through scheduled actions before flushing to emulate 2.x behavior.
  144.      * Note that the ElasticSearch index will fall out of sync with the source
  145.      * data in the event of a crash during flush.
  146.      *
  147.      * This method is only called in legacy configurations of the listener.
  148.      *
  149.      * @deprecated This method should only be called in applications that depend
  150.      *             on the behaviour that entities are indexed regardless of if a
  151.      *             flush is successful
  152.      */
  153.     public function preFlush()
  154.     {
  155.         $this->persistScheduled();
  156.     }
  157.     /**
  158.      * Iterating through scheduled actions *after* flushing ensures that the
  159.      * ElasticSearch index will be affected only if the query is successful.
  160.      */
  161.     public function postFlush()
  162.     {
  163.         $this->persistScheduled();
  164.     }
  165.     /**
  166.      * Determines whether or not it is okay to persist now.
  167.      *
  168.      * @return bool
  169.      */
  170.     private function shouldPersist()
  171.     {
  172.         return !$this->config['defer'];
  173.     }
  174.     /**
  175.      * Persist scheduled objects to ElasticSearch
  176.      * After persisting, clear the scheduled queue to prevent multiple data updates when using multiple flush calls.
  177.      */
  178.     private function persistScheduled()
  179.     {
  180.         if ($this->shouldPersist()) {
  181.             if (count($this->scheduledForInsertion)) {
  182.                 $this->objectPersister->insertMany($this->scheduledForInsertion);
  183.                 $this->scheduledForInsertion = [];
  184.             }
  185.             if (count($this->scheduledForUpdate)) {
  186.                 $this->objectPersister->replaceMany($this->scheduledForUpdate);
  187.                 $this->scheduledForUpdate = [];
  188.             }
  189.             if (count($this->scheduledForDeletion)) {
  190.                 $this->objectPersister->deleteManyByIdentifiers($this->scheduledForDeletion);
  191.                 $this->scheduledForDeletion = [];
  192.             }
  193.         }
  194.     }
  195.     /**
  196.      * Record the specified identifier to delete. Do not need to entire object.
  197.      *
  198.      * @param object $object
  199.      */
  200.     private function scheduleForDeletion($object)
  201.     {
  202.         if ($identifierValue $this->propertyAccessor->getValue($object$this->config['identifier'])) {
  203.             $this->scheduledForDeletion[] = !is_scalar($identifierValue) ? (string) $identifierValue $identifierValue;
  204.         }
  205.     }
  206.     /**
  207.      * Checks if the object is indexable or not.
  208.      *
  209.      * @param object $object
  210.      *
  211.      * @return bool
  212.      */
  213.     private function isObjectIndexable($object)
  214.     {
  215.         return $this->indexable->isObjectIndexable(
  216.             $this->config['indexName'],
  217.             $this->config['typeName'],
  218.             $object
  219.         );
  220.     }
  221. }
  222. class_exists(LifecycleEventArgs::class);