vendor/shopware/core/Content/LandingPage/SalesChannel/CachedLandingPageRoute.php line 73

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\LandingPage\SalesChannel;
  3. use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
  4. use Shopware\Core\Content\Cms\SalesChannel\Struct\ProductBoxStruct;
  5. use Shopware\Core\Content\Cms\SalesChannel\Struct\ProductSliderStruct;
  6. use Shopware\Core\Content\LandingPage\Event\LandingPageRouteCacheKeyEvent;
  7. use Shopware\Core\Content\LandingPage\Event\LandingPageRouteCacheTagsEvent;
  8. use Shopware\Core\Framework\Adapter\Cache\AbstractCacheTracer;
  9. use Shopware\Core\Framework\Adapter\Cache\CacheValueCompressor;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\RuleAreas;
  12. use Shopware\Core\Framework\Log\Package;
  13. use Shopware\Core\Framework\Util\Json;
  14. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  15. use Symfony\Component\HttpFoundation\Request;
  16. use Symfony\Component\Routing\Annotation\Route;
  17. use Symfony\Contracts\Cache\CacheInterface;
  18. use Symfony\Contracts\Cache\ItemInterface;
  19. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  20. #[Route(defaults: ['_routeScope' => ['store-api']])]
  21. #[Package('content')]
  22. class CachedLandingPageRoute extends AbstractLandingPageRoute
  23. {
  24.     /**
  25.      * @internal
  26.      *
  27.      * @param AbstractCacheTracer<LandingPageRouteResponse> $tracer
  28.      * @param array<string> $states
  29.      */
  30.     public function __construct(
  31.         private readonly AbstractLandingPageRoute $decorated,
  32.         private readonly CacheInterface $cache,
  33.         private readonly EntityCacheKeyGenerator $generator,
  34.         private readonly AbstractCacheTracer $tracer,
  35.         private readonly EventDispatcherInterface $dispatcher,
  36.         private readonly array $states
  37.     ) {
  38.     }
  39.     public static function buildName(string $id): string
  40.     {
  41.         return 'landing-page-route-' $id;
  42.     }
  43.     public function getDecorated(): AbstractLandingPageRoute
  44.     {
  45.         return $this->decorated;
  46.     }
  47.     #[Route(path'/store-api/landing-page/{landingPageId}'name'store-api.landing-page.detail'methods: ['POST'])]
  48.     public function load(string $landingPageIdRequest $requestSalesChannelContext $context): LandingPageRouteResponse
  49.     {
  50.         if ($context->hasState(...$this->states)) {
  51.             return $this->getDecorated()->load($landingPageId$request$context);
  52.         }
  53.         $key $this->generateKey($landingPageId$request$context);
  54.         if ($key === null) {
  55.             return $this->getDecorated()->load($landingPageId$request$context);
  56.         }
  57.         $value $this->cache->get($key, function (ItemInterface $item) use ($request$context$landingPageId) {
  58.             $name self::buildName($landingPageId);
  59.             $response $this->tracer->trace($name, fn () => $this->getDecorated()->load($landingPageId$request$context));
  60.             $item->tag($this->generateTags($landingPageId$response$request$context));
  61.             return CacheValueCompressor::compress($response);
  62.         });
  63.         return CacheValueCompressor::uncompress($value);
  64.     }
  65.     private function generateKey(string $landingPageIdRequest $requestSalesChannelContext $context): ?string
  66.     {
  67.         $parts = [...$request->query->all(), ...$request->request->all(), ...[$this->generator->getSalesChannelContextHash($context, [RuleAreas::LANDING_PAGE_AREARuleAreas::PRODUCT_AREARuleAreas::CATEGORY_AREA])]];
  68.         $event = new LandingPageRouteCacheKeyEvent($landingPageId$parts$request$contextnull);
  69.         $this->dispatcher->dispatch($event);
  70.         if (!$event->shouldCache()) {
  71.             return null;
  72.         }
  73.         return self::buildName($landingPageId) . '-' md5(Json::encode($event->getParts()));
  74.     }
  75.     /**
  76.      * @return array<string>
  77.      */
  78.     private function generateTags(string $landingPageIdLandingPageRouteResponse $responseRequest $requestSalesChannelContext $context): array
  79.     {
  80.         $tags array_merge(
  81.             $this->tracer->get(self::buildName($landingPageId)),
  82.             $this->extractIds($response),
  83.             [self::buildName($landingPageId)]
  84.         );
  85.         $event = new LandingPageRouteCacheTagsEvent($landingPageId$tags$request$response$contextnull);
  86.         $this->dispatcher->dispatch($event);
  87.         return array_unique(array_filter($event->getTags()));
  88.     }
  89.     /**
  90.      * @return array<string>
  91.      */
  92.     private function extractIds(LandingPageRouteResponse $response): array
  93.     {
  94.         $page $response->getLandingPage()->getCmsPage();
  95.         if ($page === null) {
  96.             return [];
  97.         }
  98.         $ids = [];
  99.         $streamIds = [];
  100.         $slots $page->getElementsOfType('product-slider');
  101.         /** @var CmsSlotEntity $slot */
  102.         foreach ($slots as $slot) {
  103.             $slider $slot->getData();
  104.             if (!$slider instanceof ProductSliderStruct) {
  105.                 continue;
  106.             }
  107.             if ($slider->getStreamId() !== null) {
  108.                 $streamIds[] = $slider->getStreamId();
  109.             }
  110.             if ($slider->getProducts() === null) {
  111.                 continue;
  112.             }
  113.             foreach ($slider->getProducts() as $product) {
  114.                 $ids[] = $product->getId();
  115.                 $ids[] = $product->getParentId();
  116.             }
  117.         }
  118.         $slots $page->getElementsOfType('product-box');
  119.         /** @var CmsSlotEntity $slot */
  120.         foreach ($slots as $slot) {
  121.             $box $slot->getData();
  122.             if (!$box instanceof ProductBoxStruct) {
  123.                 continue;
  124.             }
  125.             if ($box->getProduct() === null) {
  126.                 continue;
  127.             }
  128.             $ids[] = $box->getProduct()->getId();
  129.             $ids[] = $box->getProduct()->getParentId();
  130.         }
  131.         $ids array_values(array_unique(array_filter($ids)));
  132.         return [...array_map(EntityCacheKeyGenerator::buildProductTag(...), $ids), ...array_map(EntityCacheKeyGenerator::buildStreamTag(...), $streamIds), ...[EntityCacheKeyGenerator::buildCmsTag($page->getId())]];
  133.     }
  134. }