vendor/shopware/core/Content/Product/SalesChannel/Detail/CachedProductDetailRoute.php line 77

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Product\SalesChannel\Detail;
  3. use Shopware\Core\Content\Product\Events\ProductDetailRouteCacheKeyEvent;
  4. use Shopware\Core\Content\Product\Events\ProductDetailRouteCacheTagsEvent;
  5. use Shopware\Core\Framework\Adapter\Cache\AbstractCacheTracer;
  6. use Shopware\Core\Framework\Adapter\Cache\CacheValueCompressor;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator;
  8. use Shopware\Core\Framework\DataAbstractionLayer\FieldSerializer\JsonFieldSerializer;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  10. use Shopware\Core\Framework\Routing\Annotation\Entity;
  11. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  12. use Shopware\Core\Framework\Routing\Annotation\Since;
  13. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  14. use Symfony\Component\HttpFoundation\Request;
  15. use Symfony\Component\Routing\Annotation\Route;
  16. use Symfony\Contracts\Cache\CacheInterface;
  17. use Symfony\Contracts\Cache\ItemInterface;
  18. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  19. /**
  20.  * @Route(defaults={"_routeScope"={"store-api"}})
  21.  */
  22. class CachedProductDetailRoute extends AbstractProductDetailRoute
  23. {
  24.     private AbstractProductDetailRoute $decorated;
  25.     private CacheInterface $cache;
  26.     private EntityCacheKeyGenerator $generator;
  27.     /**
  28.      * @var AbstractCacheTracer<ProductDetailRouteResponse>
  29.      */
  30.     private AbstractCacheTracer $tracer;
  31.     /**
  32.      * @var array<string, string>
  33.      */
  34.     private array $states;
  35.     private EventDispatcherInterface $dispatcher;
  36.     /**
  37.      * @internal
  38.      *
  39.      * @param AbstractCacheTracer<ProductDetailRouteResponse> $tracer
  40.      * @param array<string> $states
  41.      */
  42.     public function __construct(
  43.         AbstractProductDetailRoute $decorated,
  44.         CacheInterface $cache,
  45.         EntityCacheKeyGenerator $generator,
  46.         AbstractCacheTracer $tracer,
  47.         EventDispatcherInterface $dispatcher,
  48.         array $states
  49.     ) {
  50.         $this->decorated $decorated;
  51.         $this->cache $cache;
  52.         $this->generator $generator;
  53.         $this->tracer $tracer;
  54.         $this->states $states;
  55.         $this->dispatcher $dispatcher;
  56.     }
  57.     public function getDecorated(): AbstractProductDetailRoute
  58.     {
  59.         return $this->decorated;
  60.     }
  61.     /**
  62.      * @Since("6.3.2.0")
  63.      * @Entity("product")
  64.      * @Route("/store-api/product/{productId}", name="store-api.product.detail", methods={"POST"})
  65.      */
  66.     public function load(string $productIdRequest $requestSalesChannelContext $contextCriteria $criteria): ProductDetailRouteResponse
  67.     {
  68.         if ($context->hasState(...$this->states)) {
  69.             return $this->getDecorated()->load($productId$request$context$criteria);
  70.         }
  71.         $key $this->generateKey($productId$request$context$criteria);
  72.         if ($key === null) {
  73.             return $this->getDecorated()->load($productId$request$context$criteria);
  74.         }
  75.         $value $this->cache->get($key, function (ItemInterface $item) use ($productId$request$context$criteria) {
  76.             $name self::buildName($productId);
  77.             $response $this->tracer->trace($name, function () use ($productId$request$context$criteria) {
  78.                 return $this->getDecorated()->load($productId$request$context$criteria);
  79.             });
  80.             $item->tag($this->generateTags($productId$request$response$context$criteria));
  81.             return CacheValueCompressor::compress($response);
  82.         });
  83.         return CacheValueCompressor::uncompress($value);
  84.     }
  85.     public static function buildName(string $parentId): string
  86.     {
  87.         return 'product-detail-route-' $parentId;
  88.     }
  89.     private function generateKey(string $productIdRequest $requestSalesChannelContext $contextCriteria $criteria): ?string
  90.     {
  91.         $parts = [
  92.             $this->generator->getCriteriaHash($criteria),
  93.             $this->generator->getSalesChannelContextHash($context),
  94.         ];
  95.         $event = new ProductDetailRouteCacheKeyEvent($parts$request$context$criteria);
  96.         $this->dispatcher->dispatch($event);
  97.         if (!$event->shouldCache()) {
  98.             return null;
  99.         }
  100.         return self::buildName($productId) . '-' md5(JsonFieldSerializer::encodeJson($event->getParts()));
  101.     }
  102.     /**
  103.      * @return array<string>
  104.      */
  105.     private function generateTags(string $productIdRequest $requestProductDetailRouteResponse $responseSalesChannelContext $contextCriteria $criteria): array
  106.     {
  107.         $parentId $response->getProduct()->getParentId() ?? $response->getProduct()->getId();
  108.         $pageId $response->getProduct()->getCmsPageId();
  109.         $tags array_merge(
  110.             $this->tracer->get(self::buildName($productId)),
  111.             [$pageId !== null EntityCacheKeyGenerator::buildCmsTag($pageId) : null],
  112.             [self::buildName($parentId)]
  113.         );
  114.         $event = new ProductDetailRouteCacheTagsEvent($tags$request$response$context$criteria);
  115.         $this->dispatcher->dispatch($event);
  116.         return array_unique(array_filter($event->getTags()));
  117.     }
  118. }