src/Storefront/Framework/Seo/SeoUrlRoute/SeoUrlUpdateListener.php line 38

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Framework\Seo\SeoUrlRoute;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Content\Category\CategoryDefinition;
  5. use Shopware\Core\Content\Category\CategoryEvents;
  6. use Shopware\Core\Content\Category\Event\CategoryIndexerEvent;
  7. use Shopware\Core\Content\LandingPage\Event\LandingPageIndexerEvent;
  8. use Shopware\Core\Content\LandingPage\LandingPageEvents;
  9. use Shopware\Core\Content\Product\Events\ProductIndexerEvent;
  10. use Shopware\Core\Content\Product\ProductEvents;
  11. use Shopware\Core\Content\Seo\SeoUrlUpdater;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Indexing\EntityIndexerRegistry;
  14. use Shopware\Core\Framework\Log\Package;
  15. use Shopware\Core\Framework\Uuid\Uuid;
  16. use Shopware\Core\System\SalesChannel\SalesChannelDefinition;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. /**
  19.  * @internal
  20.  */
  21. #[Package('sales-channel')]
  22. class SeoUrlUpdateListener implements EventSubscriberInterface
  23. {
  24.     final public const CATEGORY_SEO_URL_UPDATER 'category.seo-url';
  25.     final public const PRODUCT_SEO_URL_UPDATER 'product.seo-url';
  26.     final public const LANDING_PAGE_SEO_URL_UPDATER 'landing_page.seo-url';
  27.     /**
  28.      * @internal
  29.      */
  30.     public function __construct(private readonly SeoUrlUpdater $seoUrlUpdater, private readonly Connection $connection, private readonly EntityIndexerRegistry $indexerRegistry)
  31.     {
  32.     }
  33.     public function detectSalesChannelEntryPoints(EntityWrittenContainerEvent $event): void
  34.     {
  35.         $properties = ['navigationCategoryId''footerCategoryId''serviceCategoryId'];
  36.         $salesChannelIds $event->getPrimaryKeysWithPropertyChange(SalesChannelDefinition::ENTITY_NAME$properties);
  37.         if (empty($salesChannelIds)) {
  38.             return;
  39.         }
  40.         $this->indexerRegistry->sendIndexingMessage(['category.indexer''product.indexer']);
  41.     }
  42.     /**
  43.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  44.      */
  45.     public static function getSubscribedEvents(): array
  46.     {
  47.         return [
  48.             ProductEvents::PRODUCT_INDEXER_EVENT => 'updateProductUrls',
  49.             CategoryEvents::CATEGORY_INDEXER_EVENT => 'updateCategoryUrls',
  50.             LandingPageEvents::LANDING_PAGE_INDEXER_EVENT => 'updateLandingPageUrls',
  51.             EntityWrittenContainerEvent::class => 'detectSalesChannelEntryPoints',
  52.         ];
  53.     }
  54.     public function updateCategoryUrls(CategoryIndexerEvent $event): void
  55.     {
  56.         if (\in_array(self::CATEGORY_SEO_URL_UPDATER$event->getSkip(), true)) {
  57.             return;
  58.         }
  59.         $ids array_merge(array_values($event->getIds()), $this->getCategoryChildren($event->getIds()));
  60.         $this->seoUrlUpdater->update(NavigationPageSeoUrlRoute::ROUTE_NAME$ids);
  61.     }
  62.     public function updateProductUrls(ProductIndexerEvent $event): void
  63.     {
  64.         if (\in_array(self::PRODUCT_SEO_URL_UPDATER$event->getSkip(), true)) {
  65.             return;
  66.         }
  67.         $this->seoUrlUpdater->update(ProductPageSeoUrlRoute::ROUTE_NAMEarray_values($event->getIds()));
  68.     }
  69.     public function updateLandingPageUrls(LandingPageIndexerEvent $event): void
  70.     {
  71.         if (\in_array(self::LANDING_PAGE_SEO_URL_UPDATER$event->getSkip(), true)) {
  72.             return;
  73.         }
  74.         $this->seoUrlUpdater->update(LandingPageSeoUrlRoute::ROUTE_NAMEarray_values($event->getIds()));
  75.     }
  76.     /**
  77.      * @param list<string> $ids
  78.      *
  79.      * @return list<string>
  80.      */
  81.     private function getCategoryChildren(array $ids): array
  82.     {
  83.         if (empty($ids)) {
  84.             return [];
  85.         }
  86.         $query $this->connection->createQueryBuilder();
  87.         $query->select('category.id');
  88.         $query->from('category');
  89.         foreach ($ids as $id) {
  90.             $key 'id' $id;
  91.             $query->orWhere('category.type != :type AND category.path LIKE :' $key);
  92.             $query->setParameter($key'%' $id '%');
  93.         }
  94.         $query->setParameter('type'CategoryDefinition::TYPE_LINK);
  95.         $children $query->executeQuery()->fetchFirstColumn();
  96.         if (!$children) {
  97.             return [];
  98.         }
  99.         /** @var list<string> $ids */
  100.         $ids Uuid::fromBytesToHexList($children);
  101.         return $ids;
  102.     }
  103. }