platform/src/Core/Framework/Adapter/Cache/CacheInvalidationSubscriber.php line 254

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Adapter\Cache;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Checkout\Cart\CachedRuleLoader;
  5. use Shopware\Core\Checkout\Customer\Aggregate\CustomerGroup\CustomerGroupDefinition;
  6. use Shopware\Core\Checkout\Payment\PaymentMethodDefinition;
  7. use Shopware\Core\Checkout\Payment\SalesChannel\CachedPaymentMethodRoute;
  8. use Shopware\Core\Checkout\Shipping\SalesChannel\CachedShippingMethodRoute;
  9. use Shopware\Core\Checkout\Shipping\ShippingMethodDefinition;
  10. use Shopware\Core\Content\Category\CategoryDefinition;
  11. use Shopware\Core\Content\Category\Event\CategoryIndexerEvent;
  12. use Shopware\Core\Content\Category\SalesChannel\CachedCategoryRoute;
  13. use Shopware\Core\Content\Category\SalesChannel\CachedNavigationRoute;
  14. use Shopware\Core\Content\Cms\CmsPageDefinition;
  15. use Shopware\Core\Content\LandingPage\Event\LandingPageIndexerEvent;
  16. use Shopware\Core\Content\LandingPage\SalesChannel\CachedLandingPageRoute;
  17. use Shopware\Core\Content\Product\Aggregate\ProductCategory\ProductCategoryDefinition;
  18. use Shopware\Core\Content\Product\Aggregate\ProductCrossSelling\ProductCrossSellingDefinition;
  19. use Shopware\Core\Content\Product\Aggregate\ProductManufacturer\ProductManufacturerDefinition;
  20. use Shopware\Core\Content\Product\Aggregate\ProductProperty\ProductPropertyDefinition;
  21. use Shopware\Core\Content\Product\Events\ProductChangedEventInterface;
  22. use Shopware\Core\Content\Product\Events\ProductIndexerEvent;
  23. use Shopware\Core\Content\Product\Events\ProductNoLongerAvailableEvent;
  24. use Shopware\Core\Content\Product\ProductDefinition;
  25. use Shopware\Core\Content\Product\SalesChannel\CrossSelling\CachedProductCrossSellingRoute;
  26. use Shopware\Core\Content\Product\SalesChannel\Detail\CachedProductDetailRoute;
  27. use Shopware\Core\Content\Product\SalesChannel\Listing\CachedProductListingRoute;
  28. use Shopware\Core\Content\Product\SalesChannel\Review\CachedProductReviewRoute;
  29. use Shopware\Core\Content\ProductStream\ProductStreamDefinition;
  30. use Shopware\Core\Content\Property\Aggregate\PropertyGroupOption\PropertyGroupOptionDefinition;
  31. use Shopware\Core\Content\Property\Aggregate\PropertyGroupOptionTranslation\PropertyGroupOptionTranslationDefinition;
  32. use Shopware\Core\Content\Property\Aggregate\PropertyGroupTranslation\PropertyGroupTranslationDefinition;
  33. use Shopware\Core\Content\Property\PropertyGroupDefinition;
  34. use Shopware\Core\Content\Rule\Event\RuleIndexerEvent;
  35. use Shopware\Core\Content\Seo\CachedSeoResolver;
  36. use Shopware\Core\Content\Seo\Event\SeoUrlUpdateEvent;
  37. use Shopware\Core\Content\Sitemap\Event\SitemapGeneratedEvent;
  38. use Shopware\Core\Content\Sitemap\SalesChannel\CachedSitemapRoute;
  39. use Shopware\Core\Defaults;
  40. use Shopware\Core\Framework\Adapter\Translation\Translator;
  41. use Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator;
  42. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
  43. use Shopware\Core\Framework\Plugin\Event\PluginPostActivateEvent;
  44. use Shopware\Core\Framework\Plugin\Event\PluginPostDeactivateEvent;
  45. use Shopware\Core\Framework\Plugin\Event\PluginPostInstallEvent;
  46. use Shopware\Core\Framework\Plugin\Event\PluginPostUninstallEvent;
  47. use Shopware\Core\Framework\Plugin\Event\PluginPostUpdateEvent;
  48. use Shopware\Core\Framework\Uuid\Uuid;
  49. use Shopware\Core\System\Country\CountryDefinition;
  50. use Shopware\Core\System\Country\SalesChannel\CachedCountryRoute;
  51. use Shopware\Core\System\Currency\CurrencyDefinition;
  52. use Shopware\Core\System\Currency\SalesChannel\CachedCurrencyRoute;
  53. use Shopware\Core\System\Language\LanguageDefinition;
  54. use Shopware\Core\System\Language\SalesChannel\CachedLanguageRoute;
  55. use Shopware\Core\System\SalesChannel\Aggregate\SalesChannelCountry\SalesChannelCountryDefinition;
  56. use Shopware\Core\System\SalesChannel\Aggregate\SalesChannelCurrency\SalesChannelCurrencyDefinition;
  57. use Shopware\Core\System\SalesChannel\Aggregate\SalesChannelLanguage\SalesChannelLanguageDefinition;
  58. use Shopware\Core\System\SalesChannel\Aggregate\SalesChannelPaymentMethod\SalesChannelPaymentMethodDefinition;
  59. use Shopware\Core\System\SalesChannel\Aggregate\SalesChannelShippingMethod\SalesChannelShippingMethodDefinition;
  60. use Shopware\Core\System\SalesChannel\Context\CachedSalesChannelContextFactory;
  61. use Shopware\Core\System\SalesChannel\SalesChannelDefinition;
  62. use Shopware\Core\System\Salutation\SalesChannel\CachedSalutationRoute;
  63. use Shopware\Core\System\Salutation\SalutationDefinition;
  64. use Shopware\Core\System\Snippet\SnippetDefinition;
  65. use Shopware\Core\System\SystemConfig\CachedSystemConfigLoader;
  66. use Shopware\Core\System\SystemConfig\Event\SystemConfigChangedEvent;
  67. use Shopware\Core\System\SystemConfig\SystemConfigService;
  68. use Shopware\Core\System\Tax\TaxDefinition;
  69. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  70. /**
  71.  * @internal - The functions inside this class are no public-api and can be changed without previous deprecation
  72.  */
  73. class CacheInvalidationSubscriber implements EventSubscriberInterface
  74. {
  75.     private Connection $connection;
  76.     private CacheInvalidator $cacheInvalidator;
  77.     public function __construct(
  78.         CacheInvalidator $cacheInvalidator,
  79.         Connection $connection
  80.     ) {
  81.         $this->cacheInvalidator $cacheInvalidator;
  82.         $this->connection $connection;
  83.     }
  84.     public static function getSubscribedEvents()
  85.     {
  86.         return [
  87.             CategoryIndexerEvent::class => [
  88.                 ['invalidateCategoryRouteByCategoryIds'2000],
  89.                 ['invalidateListingRouteByCategoryIds'2001],
  90.             ],
  91.             LandingPageIndexerEvent::class => [
  92.                 ['invalidateIndexedLandingPages'2000],
  93.             ],
  94.             ProductIndexerEvent::class => [
  95.                 ['invalidateSearch'2000],
  96.                 ['invalidateListings'2001],
  97.                 ['invalidateProductIds'2002],
  98.                 ['invalidateDetailRoute'2004],
  99.                 ['invalidateStreamsAfterIndexing'2005],
  100.                 ['invalidateReviewRoute'2006],
  101.             ],
  102.             ProductNoLongerAvailableEvent::class => [
  103.                 ['invalidateSearch'2000],
  104.                 ['invalidateListings'2001],
  105.                 ['invalidateProductIds'2002],
  106.                 ['invalidateDetailRoute'2004],
  107.                 ['invalidateStreamsAfterIndexing'2005],
  108.                 ['invalidateReviewRoute'2006],
  109.             ],
  110.             EntityWrittenContainerEvent::class => [
  111.                 ['invalidateCmsPageIds'2001],
  112.                 ['invalidateCurrencyRoute'2002],
  113.                 ['invalidateLanguageRoute'2003],
  114.                 ['invalidateNavigationRoute'2004],
  115.                 ['invalidatePaymentMethodRoute'2005],
  116.                 ['invalidateProductAssignment'2006],
  117.                 ['invalidateManufacturerFilters'2007],
  118.                 ['invalidatePropertyFilters'2008],
  119.                 ['invalidateCrossSellingRoute'2009],
  120.                 ['invalidateContext'2010],
  121.                 ['invalidateShippingMethodRoute'2011],
  122.                 ['invalidateSnippets'2012],
  123.                 ['invalidateStreamsBeforeIndexing'2013],
  124.                 ['invalidateStreamIds'2014],
  125.                 ['invalidateCountryRoute'2015],
  126.                 ['invalidateSalutationRoute'2016],
  127.             ],
  128.             SeoUrlUpdateEvent::class => [
  129.                 ['invalidateSeoUrls'2000],
  130.             ],
  131.             RuleIndexerEvent::class => [
  132.                 ['invalidateRules'2000],
  133.             ],
  134.             PluginPostInstallEvent::class => [
  135.                 ['invalidateRules'2000],
  136.                 ['invalidateConfig'2001],
  137.             ],
  138.             PluginPostActivateEvent::class => [
  139.                 ['invalidateRules'2000],
  140.                 ['invalidateConfig'2001],
  141.             ],
  142.             PluginPostUpdateEvent::class => [
  143.                 ['invalidateRules'2000],
  144.                 ['invalidateConfig'2001],
  145.             ],
  146.             PluginPostDeactivateEvent::class => [
  147.                 ['invalidateRules'2000],
  148.                 ['invalidateConfig'2001],
  149.             ],
  150.             PluginPostUninstallEvent::class => [
  151.                 ['invalidateRules'2000],
  152.                 ['invalidateConfig'2001],
  153.             ],
  154.             SystemConfigChangedEvent::class => [
  155.                 ['invalidateConfigKey'2000],
  156.             ],
  157.             SitemapGeneratedEvent::class => [
  158.                 ['invalidateSitemap'2000],
  159.             ],
  160.         ];
  161.     }
  162.     public function invalidateSitemap(SitemapGeneratedEvent $event): void
  163.     {
  164.         $this->cacheInvalidator->invalidate([
  165.             CachedSitemapRoute::buildName($event->getSalesChannelContext()->getSalesChannelId()),
  166.         ]);
  167.     }
  168.     public function invalidateConfig(): void
  169.     {
  170.         // invalidates the complete cached config
  171.         $this->cacheInvalidator->invalidate([
  172.             CachedSystemConfigLoader::CACHE_TAG,
  173.         ]);
  174.     }
  175.     public function invalidateConfigKey(SystemConfigChangedEvent $event): void
  176.     {
  177.         // invalidates the complete cached config and routes which access a specific key
  178.         $this->cacheInvalidator->invalidate([
  179.             SystemConfigService::buildName($event->getKey()),
  180.             CachedSystemConfigLoader::CACHE_TAG,
  181.         ]);
  182.     }
  183.     public function invalidateSnippets(EntityWrittenContainerEvent $event): void
  184.     {
  185.         // invalidates all http cache items where the snippets used
  186.         $snippets $event->getEventByEntityName(SnippetDefinition::ENTITY_NAME);
  187.         if (!$snippets) {
  188.             return;
  189.         }
  190.         $tags = [];
  191.         foreach ($snippets->getPayloads() as $payload) {
  192.             if (isset($payload['translationKey'])) {
  193.                 $tags[] = Translator::buildName($payload['translationKey']);
  194.             }
  195.         }
  196.         $this->cacheInvalidator->invalidate($tags);
  197.     }
  198.     public function invalidateShippingMethodRoute(EntityWrittenContainerEvent $event): void
  199.     {
  200.         // checks if a shipping method changed or the assignment between shipping method and sales channel
  201.         $logs array_merge(
  202.             $this->getChangedShippingMethods($event),
  203.             $this->getChangedShippingAssignments($event)
  204.         );
  205.         $this->cacheInvalidator->invalidate($logs);
  206.     }
  207.     public function invalidateSeoUrls(SeoUrlUpdateEvent $event): void
  208.     {
  209.         // invalidates the cache for the seo url resolver based on the path infos which used for the new seo urls
  210.         $urls $event->getSeoUrls();
  211.         $pathInfo array_column($urls'pathInfo');
  212.         $this->cacheInvalidator->invalidate(array_map([CachedSeoResolver::class, 'buildName'], $pathInfo));
  213.     }
  214.     public function invalidateRules(): void
  215.     {
  216.         // invalidates the rule loader each time a rule changed or a plugin install state changed
  217.         $this->cacheInvalidator->invalidate([CachedRuleLoader::CACHE_KEY]);
  218.     }
  219.     public function invalidateCmsPageIds(EntityWrittenContainerEvent $event): void
  220.     {
  221.         // invalidates all routes and http cache pages where a cms page was loaded, the id is assigned as tag
  222.         $this->cacheInvalidator->invalidate(
  223.             array_map([EntityCacheKeyGenerator::class, 'buildCmsTag'], $event->getPrimaryKeys(CmsPageDefinition::ENTITY_NAME))
  224.         );
  225.     }
  226.     public function invalidateProductIds(ProductChangedEventInterface $event): void
  227.     {
  228.         // invalidates all routes which loads products in nested unknown objects, like cms listing elements or cross selling elements
  229.         $this->cacheInvalidator->invalidate(
  230.             array_map([EntityCacheKeyGenerator::class, 'buildProductTag'], $event->getIds())
  231.         );
  232.     }
  233.     public function invalidateStreamIds(EntityWrittenContainerEvent $event): void
  234.     {
  235.         // invalidates all routes which are loaded based on a stream (e.G. category listing and cross selling)
  236.         $this->cacheInvalidator->invalidate(
  237.             array_map([EntityCacheKeyGenerator::class, 'buildStreamTag'], $event->getPrimaryKeys(ProductStreamDefinition::ENTITY_NAME))
  238.         );
  239.     }
  240.     public function invalidateCategoryRouteByCategoryIds(CategoryIndexerEvent $event): void
  241.     {
  242.         // invalidates the category route cache when a category changed
  243.         $this->cacheInvalidator->invalidate(
  244.             array_map([CachedCategoryRoute::class, 'buildName'], $event->getIds())
  245.         );
  246.     }
  247.     public function invalidateListingRouteByCategoryIds(CategoryIndexerEvent $event): void
  248.     {
  249.         // invalidates the product listing route each time a category changed
  250.         $this->cacheInvalidator->invalidate(
  251.             array_map([CachedProductListingRoute::class, 'buildName'], $event->getIds())
  252.         );
  253.     }
  254.     public function invalidateIndexedLandingPages(LandingPageIndexerEvent $event): void
  255.     {
  256.         // invalidates the landing page route, if the corresponding landing page changed
  257.         $this->cacheInvalidator->invalidate(
  258.             array_map([CachedLandingPageRoute::class, 'buildName'], $event->getIds())
  259.         );
  260.     }
  261.     public function invalidateCurrencyRoute(EntityWrittenContainerEvent $event): void
  262.     {
  263.         // invalidates the currency route when a currency changed or an assignment between the sales channel and currency changed
  264.         $this->cacheInvalidator->invalidate(array_merge(
  265.             $this->getChangedCurrencyAssignments($event),
  266.             $this->getChangedCurrencies($event)
  267.         ));
  268.     }
  269.     public function invalidateLanguageRoute(EntityWrittenContainerEvent $event): void
  270.     {
  271.         // invalidates the language route when a language changed or an assignment between the sales channel and language changed
  272.         $this->cacheInvalidator->invalidate(array_merge(
  273.             $this->getChangedLanguageAssignments($event),
  274.             $this->getChangedLanguages($event)
  275.         ));
  276.     }
  277.     public function invalidateCountryRoute(EntityWrittenContainerEvent $event): void
  278.     {
  279.         // invalidates the country route when a country changed or an assignment between the sales channel and country changed
  280.         $this->cacheInvalidator->invalidate(array_merge(
  281.             $this->getChangedCountryAssignments($event),
  282.             $this->getChangedCountries($event),
  283.         ));
  284.     }
  285.     public function invalidateSalutationRoute(EntityWrittenContainerEvent $event): void
  286.     {
  287.         // invalidates the salutation route when a salutation changed
  288.         $this->cacheInvalidator->invalidate(array_merge(
  289.             $this->getChangedSalutations($event),
  290.         ));
  291.     }
  292.     public function invalidateNavigationRoute(EntityWrittenContainerEvent $event): void
  293.     {
  294.         // invalidates the navigation route when a category changed or the entry point configuration of an sales channel changed
  295.         $logs array_merge(
  296.             $this->getChangedCategories($event),
  297.             $this->getChangedEntryPoints($event)
  298.         );
  299.         $this->cacheInvalidator->invalidate($logs);
  300.     }
  301.     public function invalidatePaymentMethodRoute(EntityWrittenContainerEvent $event): void
  302.     {
  303.         // invalidates the payment method route when a payment method changed or an assignment between the sales channel and payment method changed
  304.         $logs array_merge(
  305.             $this->getChangedPaymentMethods($event),
  306.             $this->getChangedPaymentAssignments($event)
  307.         );
  308.         $this->cacheInvalidator->invalidate($logs);
  309.     }
  310.     public function invalidateSearch(): void
  311.     {
  312.         // invalidates the search and suggest route each time a product changed
  313.         $this->cacheInvalidator->invalidate([
  314.             'product-suggest-route',
  315.             'product-search-route',
  316.         ]);
  317.     }
  318.     public function invalidateDetailRoute(ProductChangedEventInterface $event): void
  319.     {
  320.         //invalidates the product detail route each time a product changed or if the product is no longer available (because out of stock)
  321.         $this->cacheInvalidator->invalidate(
  322.             array_map([CachedProductDetailRoute::class, 'buildName'], $event->getIds())
  323.         );
  324.     }
  325.     public function invalidateProductAssignment(EntityWrittenContainerEvent $event): void
  326.     {
  327.         //invalidates the product listing route, each time a product - category assignment changed
  328.         $ids $event->getPrimaryKeys(ProductCategoryDefinition::ENTITY_NAME);
  329.         $ids array_column($ids'categoryId');
  330.         $this->cacheInvalidator->invalidate(
  331.             array_map([CachedProductListingRoute::class, 'buildName'], $ids)
  332.         );
  333.     }
  334.     public function invalidateContext(EntityWrittenContainerEvent $event): void
  335.     {
  336.         //invalidates the context cache - each time one of the entities which are considered inside the context factory changed
  337.         $ids $event->getPrimaryKeys(SalesChannelDefinition::ENTITY_NAME);
  338.         $keys array_map([CachedSalesChannelContextFactory::class, 'buildName'], $ids);
  339.         if ($event->getEventByEntityName(CurrencyDefinition::ENTITY_NAME)) {
  340.             $keys[] = CachedSalesChannelContextFactory::ALL_TAG;
  341.         }
  342.         if ($event->getEventByEntityName(PaymentMethodDefinition::ENTITY_NAME)) {
  343.             $keys[] = CachedSalesChannelContextFactory::ALL_TAG;
  344.         }
  345.         if ($event->getEventByEntityName(ShippingMethodDefinition::ENTITY_NAME)) {
  346.             $keys[] = CachedSalesChannelContextFactory::ALL_TAG;
  347.         }
  348.         if ($event->getEventByEntityName(TaxDefinition::ENTITY_NAME)) {
  349.             $keys[] = CachedSalesChannelContextFactory::ALL_TAG;
  350.         }
  351.         if ($event->getEventByEntityName(CountryDefinition::ENTITY_NAME)) {
  352.             $keys[] = CachedSalesChannelContextFactory::ALL_TAG;
  353.         }
  354.         if ($event->getEventByEntityName(CustomerGroupDefinition::ENTITY_NAME)) {
  355.             $keys[] = CachedSalesChannelContextFactory::ALL_TAG;
  356.         }
  357.         if ($event->getEventByEntityName(LanguageDefinition::ENTITY_NAME)) {
  358.             $keys[] = CachedSalesChannelContextFactory::ALL_TAG;
  359.         }
  360.         $keys array_filter(array_unique($keys));
  361.         if (empty($keys)) {
  362.             return;
  363.         }
  364.         $this->cacheInvalidator->invalidate($keys);
  365.     }
  366.     public function invalidateManufacturerFilters(EntityWrittenContainerEvent $event): void
  367.     {
  368.         // invalidates the product listing route, each time a manufacturer changed
  369.         $ids $event->getPrimaryKeys(ProductManufacturerDefinition::ENTITY_NAME);
  370.         if (empty($ids)) {
  371.             return;
  372.         }
  373.         $ids $this->connection->fetchFirstColumn(
  374.             'SELECT DISTINCT LOWER(HEX(category_id)) as category_id
  375.              FROM product_category_tree
  376.                 INNER JOIN product ON product.id = product_category_tree.product_id AND product_category_tree.product_version_id = product.version_id
  377.              WHERE product.product_manufacturer_id IN (:ids)
  378.              AND product.version_id = :version',
  379.             ['ids' => Uuid::fromHexToBytesList($ids), 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  380.             ['ids' => Connection::PARAM_STR_ARRAY]
  381.         );
  382.         $this->cacheInvalidator->invalidate(
  383.             array_map([CachedProductListingRoute::class, 'buildName'], $ids)
  384.         );
  385.     }
  386.     public function invalidatePropertyFilters(EntityWrittenContainerEvent $event): void
  387.     {
  388.         $this->cacheInvalidator->invalidate(array_merge(
  389.             $this->getChangedPropertyFilterTags($event),
  390.             $this->getDeletedPropertyFilterTags($event)
  391.         ));
  392.     }
  393.     public function invalidateReviewRoute(ProductChangedEventInterface $event): void
  394.     {
  395.         $this->cacheInvalidator->invalidate(
  396.             array_map([CachedProductReviewRoute::class, 'buildName'], $event->getIds())
  397.         );
  398.     }
  399.     public function invalidateListings(ProductChangedEventInterface $event): void
  400.     {
  401.         // invalidates product listings which are based on the product category assignment
  402.         $this->cacheInvalidator->invalidate(
  403.             array_map([CachedProductListingRoute::class, 'buildName'], $this->getProductCategoryIds($event->getIds()))
  404.         );
  405.     }
  406.     public function invalidateStreamsBeforeIndexing(EntityWrittenContainerEvent $event): void
  407.     {
  408.         // invalidates all stream based pages and routes before the product indexer changes product_stream_mapping
  409.         $ids $event->getPrimaryKeys(ProductDefinition::ENTITY_NAME);
  410.         if (empty($ids)) {
  411.             return;
  412.         }
  413.         // invalidates product listings which are based on a product stream
  414.         $ids $this->connection->fetchFirstColumn(
  415.             'SELECT DISTINCT LOWER(HEX(product_stream_id))
  416.              FROM product_stream_mapping
  417.              WHERE product_stream_mapping.product_id IN (:ids)
  418.              AND product_stream_mapping.product_version_id = :version',
  419.             ['ids' => Uuid::fromHexToBytesList($ids), 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  420.             ['ids' => Connection::PARAM_STR_ARRAY]
  421.         );
  422.         $this->cacheInvalidator->invalidate(
  423.             array_map([EntityCacheKeyGenerator::class, 'buildStreamTag'], $ids)
  424.         );
  425.     }
  426.     public function invalidateStreamsAfterIndexing(ProductChangedEventInterface $event): void
  427.     {
  428.         // invalidates all stream based pages and routes after the product indexer changes product_stream_mapping
  429.         $ids $this->connection->fetchFirstColumn(
  430.             'SELECT DISTINCT LOWER(HEX(product_stream_id))
  431.              FROM product_stream_mapping
  432.              WHERE product_stream_mapping.product_id IN (:ids)
  433.              AND product_stream_mapping.product_version_id = :version',
  434.             ['ids' => Uuid::fromHexToBytesList($event->getIds()), 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  435.             ['ids' => Connection::PARAM_STR_ARRAY]
  436.         );
  437.         $this->cacheInvalidator->invalidate(
  438.             array_map([EntityCacheKeyGenerator::class, 'buildStreamTag'], $ids)
  439.         );
  440.     }
  441.     public function invalidateCrossSellingRoute(EntityWrittenContainerEvent $event): void
  442.     {
  443.         // invalidates the product detail route for the changed cross selling definitions
  444.         $ids $event->getPrimaryKeys(ProductCrossSellingDefinition::ENTITY_NAME);
  445.         if (empty($ids)) {
  446.             return;
  447.         }
  448.         $ids $this->connection->fetchFirstColumn(
  449.             'SELECT DISTINCT LOWER(HEX(product_id)) FROM product_cross_selling WHERE id IN (:ids)',
  450.             ['ids' => Uuid::fromHexToBytesList($ids)],
  451.             ['ids' => Connection::PARAM_STR_ARRAY]
  452.         );
  453.         $this->cacheInvalidator->invalidate(
  454.             array_map([CachedProductCrossSellingRoute::class, 'buildName'], $ids)
  455.         );
  456.     }
  457.     private function getDeletedPropertyFilterTags(EntityWrittenContainerEvent $event): array
  458.     {
  459.         // invalidates the product listing route, each time a property changed
  460.         $ids $event->getDeletedPrimaryKeys(ProductPropertyDefinition::ENTITY_NAME);
  461.         if (empty($ids)) {
  462.             return [];
  463.         }
  464.         $productIds array_column($ids'productId');
  465.         return array_merge(
  466.             array_map([CachedProductDetailRoute::class, 'buildName'], array_unique($productIds)),
  467.             array_map([CachedProductListingRoute::class, 'buildName'], $this->getProductCategoryIds($productIds))
  468.         );
  469.     }
  470.     private function getChangedPropertyFilterTags(EntityWrittenContainerEvent $event): array
  471.     {
  472.         // invalidates the product listing route and detail rule, each time a property group changed
  473.         $propertyGroupIds array_unique(array_merge(
  474.             $event->getPrimaryKeysWithPayloadIgnoringFields(PropertyGroupDefinition::ENTITY_NAME, ['id''updatedAt']),
  475.             array_column($event->getPrimaryKeysWithPayloadIgnoringFields(PropertyGroupTranslationDefinition::ENTITY_NAME, ['propertyGroupId''languageId''updatedAt']), 'propertyGroupId')
  476.         ));
  477.         // invalidates the product listing route and detail rule, each time a property option changed
  478.         $propertyOptionIds array_unique(array_merge(
  479.             $event->getPrimaryKeysWithPayloadIgnoringFields(PropertyGroupOptionDefinition::ENTITY_NAME, ['id''updatedAt']),
  480.             array_column($event->getPrimaryKeysWithPayloadIgnoringFields(PropertyGroupOptionTranslationDefinition::ENTITY_NAME, ['propertyGroupOptionId''languageId''updatedAt']), 'propertyGroupOptionId')
  481.         ));
  482.         if (empty($propertyGroupIds) && empty($propertyOptionIds)) {
  483.             return [];
  484.         }
  485.         $productIds $this->connection->fetchFirstColumn(
  486.             'SELECT product_property.product_id
  487.              FROM product_property
  488.                 LEFT JOIN property_group_option productProperties ON productProperties.id = product_property.property_group_option_id
  489.              WHERE productProperties.property_group_id IN (:ids) OR productProperties.id IN (:optionIds)
  490.              AND product_property.product_version_id = :version',
  491.             ['ids' => Uuid::fromHexToBytesList($propertyGroupIds), 'optionIds' => Uuid::fromHexToBytesList($propertyOptionIds), 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  492.             ['ids' => Connection::PARAM_STR_ARRAY'optionIds' => Connection::PARAM_STR_ARRAY]
  493.         );
  494.         $productIds array_unique(array_merge(
  495.             $productIds,
  496.             $this->connection->fetchFirstColumn(
  497.                 'SELECT product_option.product_id
  498.                  FROM product_option
  499.                     LEFT JOIN property_group_option productOptions ON productOptions.id = product_option.property_group_option_id
  500.                  WHERE productOptions.property_group_id IN (:ids) OR productOptions.id IN (:optionIds)
  501.                  AND product_option.product_version_id = :version',
  502.                 ['ids' => Uuid::fromHexToBytesList($propertyGroupIds), 'optionIds' => Uuid::fromHexToBytesList($propertyOptionIds), 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  503.                 ['ids' => Connection::PARAM_STR_ARRAY'optionIds' => Connection::PARAM_STR_ARRAY]
  504.             )
  505.         ));
  506.         if (empty($productIds)) {
  507.             return [];
  508.         }
  509.         $parentIds $this->connection->fetchFirstColumn(
  510.             'SELECT DISTINCT LOWER(HEX(COALESCE(parent_id, id)))
  511.             FROM product
  512.             WHERE id in (:productIds) AND version_id = :version',
  513.             ['productIds' => $productIds'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  514.             ['productIds' => Connection::PARAM_STR_ARRAY]
  515.         );
  516.         $categoryIds $this->connection->fetchFirstColumn(
  517.             'SELECT DISTINCT LOWER(HEX(category_id))
  518.             FROM product_category_tree
  519.             WHERE product_id in (:productIds) AND product_version_id = :version',
  520.             ['productIds' => $productIds'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  521.             ['productIds' => Connection::PARAM_STR_ARRAY]
  522.         );
  523.         return array_merge(
  524.             array_map([CachedProductDetailRoute::class, 'buildName'], array_filter($parentIds)),
  525.             array_map([CachedProductListingRoute::class, 'buildName'], array_filter($categoryIds)),
  526.         );
  527.     }
  528.     private function getProductCategoryIds(array $ids): array
  529.     {
  530.         return $this->connection->fetchFirstColumn(
  531.             'SELECT DISTINCT LOWER(HEX(category_id)) as category_id
  532.              FROM product_category_tree
  533.              WHERE product_id IN (:ids)
  534.              AND product_version_id = :version
  535.              AND category_version_id = :version',
  536.             ['ids' => Uuid::fromHexToBytesList($ids), 'version' => Uuid::fromHexToBytes(Defaults::LIVE_VERSION)],
  537.             ['ids' => Connection::PARAM_STR_ARRAY]
  538.         );
  539.     }
  540.     private function getChangedShippingMethods(EntityWrittenContainerEvent $event): array
  541.     {
  542.         $ids $event->getPrimaryKeys(ShippingMethodDefinition::ENTITY_NAME);
  543.         if (empty($ids)) {
  544.             return [];
  545.         }
  546.         $ids $this->connection->fetchFirstColumn(
  547.             'SELECT DISTINCT LOWER(HEX(sales_channel_id)) as id FROM sales_channel_shipping_method WHERE shipping_method_id IN (:ids)',
  548.             ['ids' => Uuid::fromHexToBytesList($ids)],
  549.             ['ids' => Connection::PARAM_STR_ARRAY]
  550.         );
  551.         $tags = [];
  552.         if ($event->getDeletedPrimaryKeys(ShippingMethodDefinition::ENTITY_NAME)) {
  553.             $tags[] = CachedShippingMethodRoute::ALL_TAG;
  554.         }
  555.         return array_merge($tagsarray_map([CachedShippingMethodRoute::class, 'buildName'], $ids));
  556.     }
  557.     private function getChangedShippingAssignments(EntityWrittenContainerEvent $event): array
  558.     {
  559.         //Used to detect changes to the shipping assignment of a sales channel
  560.         $ids $event->getPrimaryKeys(SalesChannelShippingMethodDefinition::ENTITY_NAME);
  561.         $ids array_column($ids'salesChannelId');
  562.         return array_map([CachedShippingMethodRoute::class, 'buildName'], $ids);
  563.     }
  564.     private function getChangedPaymentMethods(EntityWrittenContainerEvent $event): array
  565.     {
  566.         $ids $event->getPrimaryKeys(PaymentMethodDefinition::ENTITY_NAME);
  567.         if (empty($ids)) {
  568.             return [];
  569.         }
  570.         $ids $this->connection->fetchFirstColumn(
  571.             'SELECT DISTINCT LOWER(HEX(sales_channel_id)) as id FROM sales_channel_payment_method WHERE payment_method_id IN (:ids)',
  572.             ['ids' => Uuid::fromHexToBytesList($ids)],
  573.             ['ids' => Connection::PARAM_STR_ARRAY]
  574.         );
  575.         $tags = [];
  576.         if ($event->getDeletedPrimaryKeys(PaymentMethodDefinition::ENTITY_NAME)) {
  577.             $tags[] = CachedPaymentMethodRoute::ALL_TAG;
  578.         }
  579.         return array_merge($tagsarray_map([CachedPaymentMethodRoute::class, 'buildName'], $ids));
  580.     }
  581.     private function getChangedPaymentAssignments(EntityWrittenContainerEvent $event): array
  582.     {
  583.         //Used to detect changes to the language assignment of a sales channel
  584.         $ids $event->getPrimaryKeys(SalesChannelPaymentMethodDefinition::ENTITY_NAME);
  585.         $ids array_column($ids'salesChannelId');
  586.         return array_map([CachedPaymentMethodRoute::class, 'buildName'], $ids);
  587.     }
  588.     private function getChangedCategories(EntityWrittenContainerEvent $event): array
  589.     {
  590.         $ids $event->getPrimaryKeysWithPayload(CategoryDefinition::ENTITY_NAME);
  591.         if (empty($ids)) {
  592.             return [];
  593.         }
  594.         $ids array_map([CachedNavigationRoute::class, 'buildName'], $ids);
  595.         $ids[] = CachedNavigationRoute::BASE_NAVIGATION_TAG;
  596.         return $ids;
  597.     }
  598.     private function getChangedEntryPoints(EntityWrittenContainerEvent $event): array
  599.     {
  600.         $ids $event->getPrimaryKeysWithPropertyChange(
  601.             SalesChannelDefinition::ENTITY_NAME,
  602.             ['navigationCategoryId''navigationCategoryDepth''serviceCategoryId''footerCategoryId']
  603.         );
  604.         if (empty($ids)) {
  605.             return [];
  606.         }
  607.         return [CachedNavigationRoute::ALL_TAG];
  608.     }
  609.     private function getChangedCountries(EntityWrittenContainerEvent $event): array
  610.     {
  611.         $ids $event->getPrimaryKeys(CountryDefinition::ENTITY_NAME);
  612.         if (empty($ids)) {
  613.             return [];
  614.         }
  615.         //Used to detect changes to the country itself and invalidate the route for all sales channels in which the country is assigned.
  616.         $ids $this->connection->fetchFirstColumn(
  617.             'SELECT DISTINCT LOWER(HEX(sales_channel_id)) as id FROM sales_channel_country WHERE country_id IN (:ids)',
  618.             ['ids' => Uuid::fromHexToBytesList($ids)],
  619.             ['ids' => Connection::PARAM_STR_ARRAY]
  620.         );
  621.         $tags = [];
  622.         if ($event->getDeletedPrimaryKeys(CountryDefinition::ENTITY_NAME)) {
  623.             $tags[] = CachedCountryRoute::ALL_TAG;
  624.         }
  625.         return array_merge($tagsarray_map([CachedCountryRoute::class, 'buildName'], $ids));
  626.     }
  627.     private function getChangedCountryAssignments(EntityWrittenContainerEvent $event): array
  628.     {
  629.         //Used to detect changes to the country assignment of a sales channel
  630.         $ids $event->getPrimaryKeys(SalesChannelCountryDefinition::ENTITY_NAME);
  631.         $ids array_column($ids'salesChannelId');
  632.         return array_map([CachedCountryRoute::class, 'buildName'], $ids);
  633.     }
  634.     private function getChangedSalutations(EntityWrittenContainerEvent $event): array
  635.     {
  636.         $ids $event->getPrimaryKeys(SalutationDefinition::ENTITY_NAME);
  637.         if (empty($ids)) {
  638.             return [];
  639.         }
  640.         return [CachedSalutationRoute::ALL_TAG];
  641.     }
  642.     private function getChangedLanguages(EntityWrittenContainerEvent $event): array
  643.     {
  644.         $ids $event->getPrimaryKeys(LanguageDefinition::ENTITY_NAME);
  645.         if (empty($ids)) {
  646.             return [];
  647.         }
  648.         //Used to detect changes to the language itself and invalidate the route for all sales channels in which the language is assigned.
  649.         $ids $this->connection->fetchFirstColumn(
  650.             'SELECT DISTINCT LOWER(HEX(sales_channel_id)) as id FROM sales_channel_language WHERE language_id IN (:ids)',
  651.             ['ids' => Uuid::fromHexToBytesList($ids)],
  652.             ['ids' => Connection::PARAM_STR_ARRAY]
  653.         );
  654.         $tags = [];
  655.         if ($event->getDeletedPrimaryKeys(LanguageDefinition::ENTITY_NAME)) {
  656.             $tags[] = CachedLanguageRoute::ALL_TAG;
  657.         }
  658.         return array_merge($tagsarray_map([CachedLanguageRoute::class, 'buildName'], $ids));
  659.     }
  660.     private function getChangedLanguageAssignments(EntityWrittenContainerEvent $event): array
  661.     {
  662.         //Used to detect changes to the language assignment of a sales channel
  663.         $ids $event->getPrimaryKeys(SalesChannelLanguageDefinition::ENTITY_NAME);
  664.         $ids array_column($ids'salesChannelId');
  665.         return array_map([CachedLanguageRoute::class, 'buildName'], $ids);
  666.     }
  667.     private function getChangedCurrencies(EntityWrittenContainerEvent $event): array
  668.     {
  669.         $ids $event->getPrimaryKeys(CurrencyDefinition::ENTITY_NAME);
  670.         if (empty($ids)) {
  671.             return [];
  672.         }
  673.         //Used to detect changes to the currency itself and invalidate the route for all sales channels in which the currency is assigned.
  674.         $ids $this->connection->fetchFirstColumn(
  675.             'SELECT DISTINCT LOWER(HEX(sales_channel_id)) as id FROM sales_channel_currency WHERE currency_id IN (:ids)',
  676.             ['ids' => Uuid::fromHexToBytesList($ids)],
  677.             ['ids' => Connection::PARAM_STR_ARRAY]
  678.         );
  679.         $tags = [];
  680.         if ($event->getDeletedPrimaryKeys(CurrencyDefinition::ENTITY_NAME)) {
  681.             $tags[] = CachedCurrencyRoute::ALL_TAG;
  682.         }
  683.         return array_merge($tagsarray_map([CachedCurrencyRoute::class, 'buildName'], $ids));
  684.     }
  685.     private function getChangedCurrencyAssignments(EntityWrittenContainerEvent $event): array
  686.     {
  687.         //Used to detect changes to the currency assignment of a sales channel
  688.         $ids $event->getPrimaryKeys(SalesChannelCurrencyDefinition::ENTITY_NAME);
  689.         $ids array_column($ids'salesChannelId');
  690.         return array_map([CachedCurrencyRoute::class, 'buildName'], $ids);
  691.     }
  692. }