vendor/shopware/core/Framework/DataAbstractionLayer/Cache/CachedEntityReader.php line 41

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\DataAbstractionLayer\Cache;
  3. use Shopware\Core\Framework\Context;
  4. use Shopware\Core\Framework\DataAbstractionLayer\Entity;
  5. use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
  6. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Read\EntityReaderInterface;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  9. use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface;
  10. use Symfony\Contracts\Cache\ItemInterface;
  11. class CachedEntityReader implements EntityReaderInterface
  12. {
  13.     /**
  14.      * @var TagAwareAdapterInterface
  15.      */
  16.     private $cache;
  17.     /**
  18.      * @var EntityReaderInterface
  19.      */
  20.     private $decorated;
  21.     /**
  22.      * @var EntityCacheKeyGenerator
  23.      */
  24.     private $cacheKeyGenerator;
  25.     public function __construct(
  26.         TagAwareAdapterInterface $cache,
  27.         EntityReaderInterface $decorated,
  28.         EntityCacheKeyGenerator $cacheKeyGenerator
  29.     ) {
  30.         $this->cache $cache;
  31.         $this->decorated $decorated;
  32.         $this->cacheKeyGenerator $cacheKeyGenerator;
  33.     }
  34.     public function read(EntityDefinition $definitionCriteria $criteriaContext $context): EntityCollection
  35.     {
  36.         if (!$context->getUseCache()) {
  37.             return $this->decorated->read($definition$criteria$context);
  38.         }
  39.         if (\in_array($definition->getClass(), CachedEntitySearcher::BLACKLISTtrue)) {
  40.             return $this->decorated->read($definition$criteria$context);
  41.         }
  42.         if ($this->hasFilter($criteria)) {
  43.             return $this->loadFilterResult($definition$criteria$context);
  44.         }
  45.         return $this->loadResultByIds($definition$criteria$context);
  46.     }
  47.     private function loadFilterResult(EntityDefinition $definitionCriteria $criteriaContext $context)
  48.     {
  49.         //generate cache key for full read result
  50.         $key $this->cacheKeyGenerator->getReadCriteriaCacheKey($definition$criteria$context);
  51.         $item $this->cache->getItem($key);
  52.         //hit? return
  53.         if ($item->isHit()) {
  54.             return $item->get();
  55.         }
  56.         // load full result from storage
  57.         $collection $this->decorated->read($definition, clone $criteria$context);
  58.         // cache the full result
  59.         $this->cacheCollection($definition$criteria$context$collection);
  60.         // cache each entity for further id access
  61.         foreach ($collection as $entity) {
  62.             $this->cacheEntity($definition$context$criteria$entity);
  63.         }
  64.         $this->cache->commit();
  65.         return $collection;
  66.     }
  67.     private function loadResultByIds(EntityDefinition $definitionCriteria $criteriaContext $context): EntityCollection
  68.     {
  69.         //generate cache key list for multi cache get
  70.         $keys = [];
  71.         foreach ($criteria->getIds() as $id) {
  72.             $keys[] = $this->cacheKeyGenerator->getEntityContextCacheKey($id$definition$context$criteria);
  73.         }
  74.         $items $this->cache->getItems($keys);
  75.         $mapped = [];
  76.         foreach ($items as $item) {
  77.             if (!$item->isHit()) {
  78.                 continue;
  79.             }
  80.             $entity $item->get();
  81.             if ($entity instanceof Entity) {
  82.                 $mapped[$entity->getUniqueIdentifier()] = $entity;
  83.             } else {
  84.                 $mapped[$entity] = null;
  85.             }
  86.         }
  87.         $collection $definition->getCollectionClass();
  88.         /* @var EntityCollection $collection */
  89.         $collection = new $collection(array_filter($mapped));
  90.         //check which ids are not loaded from cache
  91.         $fallback array_diff(array_values($criteria->getIds()), array_keys($mapped));
  92.         if (empty($fallback)) {
  93.             //sort collection by provided id sorting
  94.             $collection->sortByIdArray($criteria->getIds());
  95.             return $collection;
  96.         }
  97.         //clone criteria to fetch missed items
  98.         $cloned = clone $criteria;
  99.         $cloned->setIds($fallback);
  100.         //load missed cache items from storage
  101.         $persistent $this->decorated->read($definition$cloned$context);
  102.         //cache all loaded items and add to collection
  103.         foreach ($persistent as $item) {
  104.             $this->cacheEntity($definition$context$criteria$item);
  105.             $collection->add($item);
  106.         }
  107.         //check if invalid ids provided and cache them with null to prevent further storage access with invalid id calls
  108.         foreach ($criteria->getIds() as $id) {
  109.             if ($collection->has($id)) {
  110.                 continue;
  111.             }
  112.             $this->cacheNull($definition$context$id);
  113.         }
  114.         $this->cache->commit();
  115.         //sort collection by provided id sorting
  116.         $collection->sortByIdArray($criteria->getIds());
  117.         return $collection;
  118.     }
  119.     private function cacheEntity(EntityDefinition $definitionContext $contextCriteria $criteriaEntity $entity): void
  120.     {
  121.         $key $this->cacheKeyGenerator->getEntityContextCacheKey(
  122.             $entity->getUniqueIdentifier(),
  123.             $definition,
  124.             $context,
  125.             $criteria
  126.         );
  127.         /** @var ItemInterface $item */
  128.         $item $this->cache->getItem($key);
  129.         $item->set($entity);
  130.         $tags $this->cacheKeyGenerator->getAssociatedTags($definition$entity$context);
  131.         /* @var EntityDefinition $definition */
  132.         $tags[] = 'entity_' $definition->getEntityName();
  133.         //add cache keys for associated data
  134.         $item->tag($tags);
  135.         //deferred saves are persisted with the cache->commit()
  136.         $this->cache->saveDeferred($item);
  137.     }
  138.     private function cacheNull(EntityDefinition $definitionContext $contextstring $id): void
  139.     {
  140.         $key $this->cacheKeyGenerator->getEntityContextCacheKey(
  141.             $id,
  142.             $definition,
  143.             $context
  144.         );
  145.         /** @var ItemInterface $item */
  146.         $item $this->cache->getItem($key);
  147.         $item->set($id);
  148.         $entityTag $definition->getEntityName() . '.id';
  149.         $item->tag([$key$entityTag]);
  150.         //deferred saves are persisted with the cache->commit()
  151.         $this->cache->saveDeferred($item);
  152.     }
  153.     private function cacheCollection(EntityDefinition $definitionCriteria $criteriaContext $contextEntityCollection $entityCollection): void
  154.     {
  155.         $key $this->cacheKeyGenerator->getReadCriteriaCacheKey($definition$criteria$context);
  156.         /** @var ItemInterface $item */
  157.         $item $this->cache->getItem($key);
  158.         $item->set($entityCollection);
  159.         $tags = [];
  160.         foreach ($entityCollection as $entity) {
  161.             $tags array_merge($tags$this->cacheKeyGenerator->getAssociatedTags($definition$entity$context));
  162.         }
  163.         $tags array_merge($tags$this->cacheKeyGenerator->getSearchTags($definition$criteria));
  164.         //add cache keys for associated data
  165.         $item->tag($tags);
  166.         //deferred saves are persisted with the cache->commit()
  167.         $this->cache->saveDeferred($item);
  168.     }
  169.     private function hasFilter(Criteria $criteria): bool
  170.     {
  171.         return $criteria->getFilters() || $criteria->getPostFilters();
  172.     }
  173. }