<?php declare(strict_types=1);
namespace Swag\BasicExample\Core\Content\Example\SalesChannel;
use OpenApi\Annotations as OA;
use Psr\Log\LoggerInterface;
use Shopware\Core\Framework\Adapter\Cache\CacheStateSubscriber;use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\Framework\Adapter\Cache\AbstractCacheTracer;
use Shopware\Core\Framework\Adapter\Cache\CacheCompressor;
use Shopware\Core\Framework\DataAbstractionLayer\Cache\EntityCacheKeyGenerator;
use Shopware\Core\Framework\DataAbstractionLayer\FieldSerializer\JsonFieldSerializer;
use Shopware\Core\Framework\Routing\Annotation\Entity;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Routing\Annotation\Since;
use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
* @Route(defaults={"_routeScope"={"store-api"}})
class CachedExampleRoute extends AbstractExampleRoute
private AbstractExampleRoute $decorated;
private TagAwareAdapterInterface $cache;
private EntityCacheKeyGenerator $generator;
private AbstractCacheTracer $tracer;
private LoggerInterface $logger;
public function __construct(
AbstractExampleRoute $decorated,
TagAwareAdapterInterface $cache,
EntityCacheKeyGenerator $generator,
AbstractCacheTracer $tracer,
$this->decorated = $decorated;
$this->generator = $generator;
// declares that this route can not be cached if the customer is logged in
$this->states = [CacheStateSubscriber::STATE_LOGGED_IN];
public function getDecorated(): AbstractExampleRoute
* @Entity("swag_example")
* summary="This route can be used to load the swag_example by specific filters",
* operationId="readExample",
* tags={"Store API", "Example"},
* @OA\Parameter(name="Api-Basic-Parameters"),
* @OA\JsonContent(type="object",
* description="Total amount"
* property="aggregations",
* description="aggregation result"
* @OA\Items(ref="#/components/schemas/swag_example_flat")
* @Route("/store-api/example", name="store-api.example.search", methods={"GET", "POST"})
public function load(Criteria $criteria, SalesChannelContext $context): ExampleRouteResponse
// The context is provided with a state where the route cannot be cached
if ($context->hasState(...$this->states)) {
return $this->getDecorated()->load($criteria, $context);
// Fetch item from the cache pool
$item = $this->cache->getItem(
$this->generateKey($criteria, $context)
if ($item->isHit() && $item->get()) {
// Use cache compressor to uncompress the cache value
return CacheCompressor::uncompress($item);
} catch (\Throwable $e) {
// Something went wrong when uncompress the cache item - we log the error and continue to overwrite the invalid cache item
$this->logger->error($e->getMessage());
$name = self::buildName();
// start tracing of nested cache tags and system config keys
$response = $this->tracer->trace($name, function () use ($criteria, $context) {
return $this->getDecorated()->load($criteria, $context);
// compress cache content to reduce cache size
$item = CacheCompressor::compress($item, $response);
// get traced tags and configs
$this->tracer->get(self::buildName()),
$this->cache->save($item);
public static function buildName(): string
private function generateKey(SalesChannelContext $context, Criteria $criteria): string
// generate a hash for the route criteria
$this->generator->getCriteriaHash($criteria),
// generate a hash for the current context
$this->generator->getSalesChannelContextHash($context),
return md5(JsonFieldSerializer::encodeJson($parts));