src/Core/Content/Product/SalesChannel/Review/CachedProductReviewRoute.php line 65

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Product\SalesChannel\Review;
  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\CacheStateSubscriber;
  7. use Shopware\Core\Framework\Adapter\Cache\CacheValueCompressor;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\RuleAreas;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  11. use Shopware\Core\Framework\Log\Package;
  12. use Shopware\Core\Framework\Util\Json;
  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. #[Route(defaults: ['_routeScope' => ['store-api']])]
  20. #[Package('inventory')]
  21. class CachedProductReviewRoute extends AbstractProductReviewRoute
  22. {
  23.     final public const ALL_TAG 'product-review-route';
  24.     /**
  25.      * @var string[]
  26.      */
  27.     private readonly array $states;
  28.     /**
  29.      * @internal
  30.      *
  31.      * @param AbstractCacheTracer<ProductReviewRouteResponse> $tracer
  32.      * @param string[] $states
  33.      */
  34.     public function __construct(
  35.         private readonly AbstractProductReviewRoute $decorated,
  36.         private readonly CacheInterface $cache,
  37.         private readonly EntityCacheKeyGenerator $generator,
  38.         private readonly AbstractCacheTracer $tracer,
  39.         private readonly EventDispatcherInterface $dispatcher,
  40.         array $states
  41.     ) {
  42.         $states[] = CacheStateSubscriber::STATE_LOGGED_IN;
  43.         $this->states array_unique($states);
  44.     }
  45.     public function getDecorated(): AbstractProductReviewRoute
  46.     {
  47.         return $this->decorated;
  48.     }
  49.     #[Route(path'/store-api/product/{productId}/reviews'name'store-api.product-review.list'methods: ['POST'], defaults: ['_entity' => 'product_review'])]
  50.     public function load(string $productIdRequest $requestSalesChannelContext $contextCriteria $criteria): ProductReviewRouteResponse
  51.     {
  52.         if ($context->hasState(...$this->states)) {
  53.             return $this->getDecorated()->load($productId$request$context$criteria);
  54.         }
  55.         $key $this->generateKey($productId$request$context$criteria);
  56.         $value $this->cache->get($key, function (ItemInterface $item) use ($productId$request$context$criteria) {
  57.             $name self::buildName($productId);
  58.             $response $this->tracer->trace($name, fn () => $this->getDecorated()->load($productId$request$context$criteria));
  59.             $item->tag($this->generateTags($productId$request$response$context$criteria));
  60.             return CacheValueCompressor::compress($response);
  61.         });
  62.         return CacheValueCompressor::uncompress($value);
  63.     }
  64.     public static function buildName(string $productId): string
  65.     {
  66.         return 'product-review-route-' $productId;
  67.     }
  68.     private function generateKey(string $productIdRequest $requestSalesChannelContext $contextCriteria $criteria): string
  69.     {
  70.         $parts = [
  71.             $this->generator->getCriteriaHash($criteria),
  72.             $this->generator->getSalesChannelContextHash($context, [RuleAreas::PRODUCT_AREA]),
  73.         ];
  74.         $event = new ProductDetailRouteCacheKeyEvent($parts$request$context$criteria);
  75.         $this->dispatcher->dispatch($event);
  76.         return self::buildName($productId) . '-' md5(Json::encode($event->getParts()));
  77.     }
  78.     /**
  79.      * @return string[]
  80.      */
  81.     private function generateTags(string $productIdRequest $requestProductReviewRouteResponse $responseSalesChannelContext $contextCriteria $criteria): array
  82.     {
  83.         $tags array_merge(
  84.             $this->tracer->get(self::buildName($productId)),
  85.             [self::buildName($productId)]
  86.         );
  87.         $event = new ProductDetailRouteCacheTagsEvent($tags$request$response$context$criteria);
  88.         $this->dispatcher->dispatch($event);
  89.         return array_unique(array_filter($event->getTags()));
  90.     }
  91. }