Skip to content

Commit

Permalink
Merge pull request #150 from delyriand/fix/reindex-events
Browse files Browse the repository at this point in the history
Fix the reindex events, use the sylius resource event to reindex product
  • Loading branch information
maximehuran authored Dec 6, 2022
2 parents 376219a + d4784c8 commit 4023f40
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 137 deletions.
73 changes: 73 additions & 0 deletions src/EventListener/ProductEventListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

/*
* This file is part of Monsieur Biz' Search plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusSearchPlugin\EventListener;

use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds;
use MonsieurBiz\SyliusSearchPlugin\Message\ProductToDeleteFromIds;
use Sylius\Component\Core\Model\ProductInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Symfony\Component\Messenger\MessageBusInterface;
use Webmozart\Assert\Assert;

final class ProductEventListener
{
private array $productIdsToDelete = [];

private MessageBusInterface $messageBus;

public function __construct(MessageBusInterface $messageBus)
{
$this->messageBus = $messageBus;
}

public function dispatchProductReindexMessage(GenericEvent $event): void
{
/** @var ProductInterface $product */
$product = $event->getSubject();
Assert::isInstanceOf($product, ProductInterface::class);

$productReindexFromIdsMessage = new ProductReindexFromIds();
$productReindexFromIdsMessage->addProductId($product->getId());

$this->messageBus->dispatch($productReindexFromIdsMessage);
}

public function saveProductIdToDispatchReindexMessage(GenericEvent $event): void
{
/** @var ProductInterface $product */
$product = $event->getSubject();
Assert::isInstanceOf($product, ProductInterface::class);

$this->productIdsToDelete[] = $product->getId();
}

public function dispatchDeleteProductReindexMessage(GenericEvent $event): void
{
/** @var ProductInterface $product */
$product = $event->getSubject();
Assert::isInstanceOf($product, ProductInterface::class);

if (empty($this->productIdsToDelete)) {
return;
}

$productReindexFromIdsMessage = new ProductToDeleteFromIds();
foreach ($this->productIdsToDelete as $productIdToDelete) {
$productReindexFromIdsMessage->addProductId($productIdToDelete);
}

$this->productIdsToDelete = [];
$this->messageBus->dispatch($productReindexFromIdsMessage);
}
}
81 changes: 81 additions & 0 deletions src/EventListener/ProductVariantEventListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

/*
* This file is part of Monsieur Biz' Search plugin for Sylius.
*
* (c) Monsieur Biz <[email protected]>
*
* For the full copyright and license information, please view the LICENSE.txt
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace MonsieurBiz\SyliusSearchPlugin\EventListener;

use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds;
use Sylius\Component\Core\Model\ProductVariantInterface;
use Symfony\Component\EventDispatcher\GenericEvent;
use Symfony\Component\Messenger\MessageBusInterface;
use Webmozart\Assert\Assert;

final class ProductVariantEventListener
{
private array $productIdsToReindex = [];

private MessageBusInterface $messageBus;

public function __construct(MessageBusInterface $messageBus)
{
$this->messageBus = $messageBus;
}

public function dispatchProductVariantReindexMessage(GenericEvent $event): void
{
/** @var ProductVariantInterface $variant */
$variant = $event->getSubject();
Assert::isInstanceOf($variant, ProductVariantInterface::class);

if (null === $product = $variant->getProduct()) {
return;
}

$productReindexFromIdsMessage = new ProductReindexFromIds();
$productReindexFromIdsMessage->addProductId($product->getId());

$this->messageBus->dispatch($productReindexFromIdsMessage);
}

public function saveProductIdToDispatchReindexMessage(GenericEvent $event): void
{
/** @var ProductVariantInterface $variant */
$variant = $event->getSubject();
Assert::isInstanceOf($variant, ProductVariantInterface::class);

if (null === $product = $variant->getProduct()) {
return;
}

$this->productIdsToReindex[] = $product->getId();
}

public function dispatchProductReindexMessage(GenericEvent $event): void
{
/** @var ProductVariantInterface $variant */
$variant = $event->getSubject();
Assert::isInstanceOf($variant, ProductVariantInterface::class);

if (empty($this->productIdsToReindex)) {
return;
}

$productReindexFromIdsMessage = new ProductReindexFromIds();
foreach ($this->productIdsToReindex as $productIdToReindex) {
$productReindexFromIdsMessage->addProductId($productIdToReindex);
}

$this->productIdsToReindex = [];
// @TODO For the moment, this message doesn't work. We send product id but the product is deleted … Refactor the code to use document ids!
// $this->messageBus->dispatch($productReindexFromIdsMessage);
}
}
144 changes: 7 additions & 137 deletions src/EventSubscriber/ReindexProductEventSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,27 @@

use Doctrine\Bundle\DoctrineBundle\EventSubscriber\EventSubscriberInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
use Doctrine\ORM\Events;
use Doctrine\ORM\UnitOfWork;
use MonsieurBiz\SyliusSearchPlugin\Manager\AutomaticReindexManagerInterface;
use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromIds;
use MonsieurBiz\SyliusSearchPlugin\Message\ProductReindexFromTaxon;
use MonsieurBiz\SyliusSearchPlugin\Message\ProductToDeleteFromIds;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Sylius\Component\Core\Model\ChannelPricingInterface;
use Sylius\Component\Core\Model\ProductImageInterface;
use Sylius\Component\Core\Model\ProductInterface;
use Sylius\Component\Core\Model\ProductTaxonInterface;
use Sylius\Component\Core\Model\ProductTranslationInterface;
use Sylius\Component\Product\Model\ProductAttributeValueInterface;
use Sylius\Component\Product\Model\ProductInterface as ModelProductInterface;
use Sylius\Component\Product\Model\ProductVariantInterface;
use Sylius\Component\Product\Model\ProductVariantTranslationInterface;
use Symfony\Component\Messenger\MessageBusInterface;

/**
* This event subscriber only manages product taxons modifications.
* For the other entities, we use the event listener and the event sylius (pre/post).
*/
class ReindexProductEventSubscriber implements EventSubscriberInterface, LoggerAwareInterface
{
use LoggerAwareTrait;

/**
* @var ModelProductInterface[]
*/
private array $productsToReindex = [];

private array $productsToBeDelete = [];

private MessageBusInterface $messageBus;

private AutomaticReindexManagerInterface $automaticReindexManager;

private bool $dispatched = false;

public function __construct(MessageBusInterface $messageBus, AutomaticReindexManagerInterface $automaticReindexManager)
{
$this->messageBus = $messageBus;
Expand All @@ -62,8 +46,6 @@ public function getSubscribedEvents()
{
return [
Events::onFlush => 'onFlush',
Events::postFlush => 'postFlush',
Events::onClear => 'onClear',
];
}

Expand All @@ -78,125 +60,13 @@ public function onFlush(OnFlushEventArgs $eventArgs): void
$this->manageUnitOfWork($unitOfWork);
}

public function postFlush(PostFlushEventArgs $args): void
{
if (!$this->automaticReindexManager->shouldBeAutomaticallyReindex()) {
return;
}

$unitOfWork = $args->getEntityManager()->getUnitOfWork();
$this->manageUnitOfWork($unitOfWork);

$productReindexFromIdsMessage = new ProductReindexFromIds();

foreach ($this->productsToReindex as $productsToReindex) {
if (null === $productsToReindex->getId()) {
continue;
}
$productReindexFromIdsMessage->addProductId($productsToReindex->getId());
}
$this->productsToReindex = [];

if (0 !== \count($productReindexFromIdsMessage->getProductIds()) && false === $this->dispatched) {
$this->dispatched = true; // Needed to set before dispatch to avoid infinite calls by message flush containing product
$this->messageBus->dispatch($productReindexFromIdsMessage);
}
}

public function onClear(): void
{
$this->dispatched = false;
}

private function onFlushEntities(array $entities, string $type = 'insertionsOrUpdate'): void
{
foreach ($entities as $entity) {
if ($entity instanceof ProductInterface && 'deletions' === $type) {
$this->productsToBeDelete[$entity->getId()] = $entity;

continue;
}
if ($entity instanceof ProductTaxonInterface && null !== $entity->getTaxon()) {
$this->messageBus->dispatch(new ProductReindexFromTaxon($entity->getTaxon()->getId()));

continue;
}
$product = $this->getProduct($entity);
if (null !== $product) {
$this->productsToReindex[$product->getId()] = $product;
}
}
}

private function getProduct(object $entity): ?ModelProductInterface
{
switch (true) {
case $entity instanceof ProductInterface:
return $entity;
case $entity instanceof ProductVariantInterface:
return $entity->getProduct();
case $entity instanceof ProductTaxonInterface:
return $entity->getProduct();
case $entity instanceof ProductTranslationInterface && $entity->getTranslatable() instanceof ProductInterface:
/** @var ProductInterface $product */
$product = $entity->getTranslatable();

return $product;
case $entity instanceof ProductAttributeValueInterface:
return $entity->getProduct();
case $entity instanceof ProductImageInterface && $entity->getOwner() instanceof ProductInterface:
/** @var ProductInterface $product */
$product = $entity->getOwner();

return $product;
case $entity instanceof ChannelPricingInterface && $entity->getProductVariant() instanceof ProductVariantInterface:
/** @var ProductVariantInterface $productVariant */
$productVariant = $entity->getProductVariant();

return $productVariant->getProduct();
case $entity instanceof ProductVariantTranslationInterface && $entity->getTranslatable() instanceof ProductVariantInterface:
/** @var ProductVariantInterface $productVariant */
$productVariant = $entity->getTranslatable();

return $productVariant->getProduct();
}

return null;
}

private function manageUnitOfWork(UnitOfWork $unitOfWork): void
{
$collections = array_merge($unitOfWork->getScheduledCollectionUpdates(), $unitOfWork->getScheduledCollectionDeletions());
foreach ($collections as $collection) {
if (method_exists($collection, 'getOwner') && $collection->getOwner() instanceof ProductInterface) {
$product = $collection->getOwner();
$this->productsToReindex[$product->getId()] = $product;
}
}

$entities = array_merge($unitOfWork->getScheduledEntityInsertions(), $unitOfWork->getScheduledEntityUpdates());
$this->onFlushEntities($entities);
$this->onFlushEntities($unitOfWork->getScheduledEntityDeletions(), 'deletions');

if (0 !== \count($this->productsToBeDelete)) {
$productToDeleteMessage = new ProductToDeleteFromIds();
array_map(function (ProductInterface $product) use ($productToDeleteMessage): void {
foreach ($this->productsToReindex as $key => $productsToReindex) {
if ($productsToReindex->getId() === $product->getId()) {
unset($this->productsToReindex[$key]);
}
}
if (null !== $product->getId()) {
$productToDeleteMessage->addProductId($product->getId());
}
}, $this->productsToBeDelete);

if (!empty($productToDeleteMessage->getProductIds())) {
$this->messageBus->dispatch($productToDeleteMessage);
foreach ($entities as $entity) {
if ($entity instanceof ProductTaxonInterface && null !== $taxon = $entity->getTaxon()) {
$this->messageBus->dispatch(new ProductReindexFromTaxon($taxon->getId()));
}
}

// in other event subscriber ...
// todo reindex all data when: change/create/remove attribute/option, add/remove channel, add/remove locale
}
}
15 changes: 15 additions & 0 deletions src/Resources/config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,18 @@ services:
MonsieurBiz\SyliusSearchPlugin\Context\ChannelSimulationContext:
tags:
- { name: sylius.context.channel, priority: 128 }

# Event listeners
MonsieurBiz\SyliusSearchPlugin\EventListener\ProductEventListener:
tags:
- { name: kernel.event_listener, event: sylius.product.post_create, method: dispatchProductReindexMessage }
- { name: kernel.event_listener, event: sylius.product.post_update, method: dispatchProductReindexMessage }
- { name: kernel.event_listener, event: sylius.product.pre_delete, method: saveProductIdToDispatchReindexMessage }
- { name: kernel.event_listener, event: sylius.product.post_delete, method: dispatchDeleteProductReindexMessage }

MonsieurBiz\SyliusSearchPlugin\EventListener\ProductVariantEventListener:
tags:
- { name: kernel.event_listener, event: sylius.product_variant.post_create, method: dispatchProductVariantReindexMessage }
- { name: kernel.event_listener, event: sylius.product_variant.post_update, method: dispatchProductVariantReindexMessage }
- { name: kernel.event_listener, event: sylius.product_variant.pre_delete, method: saveProductIdToDispatchReindexMessage }
- { name: kernel.event_listener, event: sylius.product_variant.post_delete, method: dispatchProductReindexMessage }

0 comments on commit 4023f40

Please sign in to comment.