vendor/shopware/core/Content/Cms/DataResolver/CmsSlotsDataResolver.php line 95

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Cms\DataResolver;
  3. use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotCollection;
  4. use Shopware\Core\Content\Cms\Aggregate\CmsSlot\CmsSlotEntity;
  5. use Shopware\Core\Content\Cms\DataResolver\Element\CmsElementResolverInterface;
  6. use Shopware\Core\Content\Cms\DataResolver\Element\ElementDataCollection;
  7. use Shopware\Core\Content\Cms\DataResolver\ResolverContext\ResolverContext;
  8. use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Entity;
  10. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  11. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
  15. use Shopware\Core\Framework\Struct\ArrayEntity;
  16. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  17. class CmsSlotsDataResolver
  18. {
  19.     /**
  20.      * @var CmsElementResolverInterface[]
  21.      */
  22.     private $resolvers;
  23.     /**
  24.      * @var array
  25.      */
  26.     private $repositories;
  27.     /**
  28.      * @var DefinitionInstanceRegistry
  29.      */
  30.     private $definitionRegistry;
  31.     /**
  32.      * @param CmsElementResolverInterface[] $resolvers
  33.      */
  34.     public function __construct(iterable $resolvers, array $repositoriesDefinitionInstanceRegistry $definitionRegistry)
  35.     {
  36.         $this->definitionRegistry $definitionRegistry;
  37.         foreach ($repositories as $entityName => $repository) {
  38.             $this->repositories[$entityName] = $repository;
  39.         }
  40.         foreach ($resolvers as $resolver) {
  41.             $this->resolvers[$resolver->getType()] = $resolver;
  42.         }
  43.     }
  44.     public function resolve(CmsSlotCollection $slotsResolverContext $resolverContext): CmsSlotCollection
  45.     {
  46.         $slotCriteriaList = [];
  47.         /*
  48.          * Collect criteria objects for each slot from resolver
  49.          *
  50.          * @var CmsSlotEntity
  51.          */
  52.         foreach ($slots as $slot) {
  53.             $resolver $this->resolvers[$slot->getType()] ?? null;
  54.             if (!$resolver) {
  55.                 continue;
  56.             }
  57.             $collection $resolver->collect($slot$resolverContext);
  58.             if ($collection === null) {
  59.                 continue;
  60.             }
  61.             $slotCriteriaList[$slot->getUniqueIdentifier()] = $collection;
  62.         }
  63.         // reduce search requests by combining mergeable criteria objects
  64.         [$directReads$searches] = $this->optimizeCriteriaObjects($slotCriteriaList);
  65.         // fetch data from storage
  66.         $entities $this->fetchByIdentifier($directReads$resolverContext->getSalesChannelContext());
  67.         $searchResults $this->fetchByCriteria($searches$resolverContext->getSalesChannelContext());
  68.         // create result for each slot with the requested data
  69.         foreach ($slots as $slotId => $slot) {
  70.             $resolver $this->resolvers[$slot->getType()] ?? null;
  71.             if (!$resolver) {
  72.                 continue;
  73.             }
  74.             $result = new ElementDataCollection();
  75.             $this->mapSearchResults($result$slot$slotCriteriaList$searchResults);
  76.             $this->mapEntities($result$slot$slotCriteriaList$entities);
  77.             $resolver->enrich($slot$resolverContext$result);
  78.             // replace with return value from enrich(), because it's allowed to change the entity type
  79.             $slots->set($slotId$slot);
  80.         }
  81.         return $slots;
  82.     }
  83.     /**
  84.      * @param string[][] $directReads
  85.      *
  86.      * @throws InconsistentCriteriaIdsException
  87.      *
  88.      * @return EntitySearchResult[]
  89.      */
  90.     private function fetchByIdentifier(array $directReadsSalesChannelContext $context): array
  91.     {
  92.         $entities = [];
  93.         foreach ($directReads as $definitionClass => $ids) {
  94.             $definition $this->definitionRegistry->get($definitionClass);
  95.             $repository $this->getSalesChannelApiRepository($definition);
  96.             if ($repository) {
  97.                 $entities[$definitionClass] = $repository->search(new Criteria($ids), $context);
  98.             } else {
  99.                 $repository $this->getApiRepository($definition);
  100.                 $entities[$definitionClass] = $repository->search(new Criteria($ids), $context->getContext());
  101.             }
  102.         }
  103.         return $entities;
  104.     }
  105.     private function fetchByCriteria(array $searchesSalesChannelContext $context): array
  106.     {
  107.         $searchResults = [];
  108.         /** @var Criteria[] $criteriaObjects */
  109.         foreach ($searches as $definitionClass => $criteriaObjects) {
  110.             foreach ($criteriaObjects as $criteriaHash => $criteria) {
  111.                 $definition $this->definitionRegistry->get($definitionClass);
  112.                 $repository $this->getSalesChannelApiRepository($definition);
  113.                 if ($repository) {
  114.                     $result $repository->search($criteria$context);
  115.                 } else {
  116.                     $repository $this->getApiRepository($definition);
  117.                     $result $repository->search($criteria$context->getContext());
  118.                 }
  119.                 $searchResults[$criteriaHash] = $result;
  120.             }
  121.         }
  122.         return $searchResults;
  123.     }
  124.     /**
  125.      * @param CriteriaCollection[] $criteriaCollections
  126.      */
  127.     private function optimizeCriteriaObjects(array $criteriaCollections): array
  128.     {
  129.         $directReads = [];
  130.         $searches = [];
  131.         $criteriaCollection $this->flattenCriteriaCollections($criteriaCollections);
  132.         foreach ($criteriaCollection as $definition => $criteriaObjects) {
  133.             $directReads[$definition] = [[]];
  134.             $searches[$definition] = [];
  135.             /** @var Criteria $criteria */
  136.             foreach ($criteriaObjects as $criteria) {
  137.                 if ($this->canBeMerged($criteria)) {
  138.                     $directReads[$definition][] = $criteria->getIds();
  139.                 } else {
  140.                     $criteriaHash $this->hash($criteria);
  141.                     $criteria->addExtension('criteriaHash', new ArrayEntity(['hash' => $criteriaHash]));
  142.                     $searches[$definition][$criteriaHash] = $criteria;
  143.                 }
  144.             }
  145.         }
  146.         foreach ($directReads as $definition => $idLists) {
  147.             $directReads[$definition] = array_merge(...$idLists);
  148.         }
  149.         return [
  150.             array_filter($directReads),
  151.             array_filter($searches),
  152.         ];
  153.     }
  154.     private function canBeMerged(Criteria $criteria): bool
  155.     {
  156.         //paginated lists must be an own search
  157.         if ($criteria->getOffset() !== null || $criteria->getLimit() !== null) {
  158.             return false;
  159.         }
  160.         //sortings must be an own search
  161.         if (\count($criteria->getSorting())) {
  162.             return false;
  163.         }
  164.         //queries must be an own search
  165.         if (\count($criteria->getQueries())) {
  166.             return false;
  167.         }
  168.         if ($criteria->getAssociations()) {
  169.             return false;
  170.         }
  171.         if ($criteria->getAggregations()) {
  172.             return false;
  173.         }
  174.         $filters array_merge(
  175.             $criteria->getFilters(),
  176.             $criteria->getPostFilters()
  177.         );
  178.         // any kind of filters must be an own search
  179.         if (!empty($filters)) {
  180.             return false;
  181.         }
  182.         if (empty($filters) && empty($criteria->getIds())) {
  183.             return false;
  184.         }
  185.         return true;
  186.     }
  187.     private function getApiRepository(EntityDefinition $definition): EntityRepositoryInterface
  188.     {
  189.         return $this->definitionRegistry->getRepository($definition->getEntityName());
  190.     }
  191.     /**
  192.      * @return mixed|null
  193.      */
  194.     private function getSalesChannelApiRepository(EntityDefinition $definition)
  195.     {
  196.         return $this->repositories[$definition->getEntityName()] ?? null;
  197.     }
  198.     private function flattenCriteriaCollections(array $criteriaCollections): array
  199.     {
  200.         $flattened = [];
  201.         $criteriaCollections array_values($criteriaCollections);
  202.         foreach ($criteriaCollections as $collections) {
  203.             foreach ($collections as $definition => $criteriaObjects) {
  204.                 $flattened[$definition] = array_merge($flattened[$definition] ?? [], array_values($criteriaObjects));
  205.             }
  206.         }
  207.         return $flattened;
  208.     }
  209.     /**
  210.      * @param CriteriaCollection[] $criteriaObjects
  211.      * @param EntitySearchResult[] $searchResults
  212.      */
  213.     private function mapSearchResults(ElementDataCollection $resultCmsSlotEntity $slot, array $criteriaObjects, array $searchResults): void
  214.     {
  215.         if (!isset($criteriaObjects[$slot->getUniqueIdentifier()])) {
  216.             return;
  217.         }
  218.         foreach ($criteriaObjects[$slot->getUniqueIdentifier()] as $criterias) {
  219.             foreach ($criterias as $key => $criteria) {
  220.                 if (!$criteria->hasExtension('criteriaHash')) {
  221.                     continue;
  222.                 }
  223.                 /** @var ArrayEntity $hashArrayEntity */
  224.                 $hashArrayEntity $criteria->getExtension('criteriaHash');
  225.                 $hash $hashArrayEntity->get('hash');
  226.                 if (!isset($searchResults[$hash])) {
  227.                     continue;
  228.                 }
  229.                 $result->add($key$searchResults[$hash]);
  230.             }
  231.         }
  232.     }
  233.     /**
  234.      * @param CriteriaCollection[] $criteriaObjects
  235.      * @param EntitySearchResult[] $entities
  236.      */
  237.     private function mapEntities(ElementDataCollection $resultCmsSlotEntity $slot, array $criteriaObjects, array $entities): void
  238.     {
  239.         if (!isset($criteriaObjects[$slot->getUniqueIdentifier()])) {
  240.             return;
  241.         }
  242.         foreach ($criteriaObjects[$slot->getUniqueIdentifier()] as $definition => $criterias) {
  243.             foreach ($criterias as $key => $criteria) {
  244.                 if (!$this->canBeMerged($criteria)) {
  245.                     continue;
  246.                 }
  247.                 if (!isset($entities[$definition])) {
  248.                     continue;
  249.                 }
  250.                 $ids $criteria->getIds();
  251.                 $filtered $entities[$definition]->filter(function (Entity $entity) use ($ids) {
  252.                     return \in_array($entity->getUniqueIdentifier(), $idstrue);
  253.                 });
  254.                 $result->add($key$filtered);
  255.             }
  256.         }
  257.     }
  258.     private function hash(Criteria $criteria): string
  259.     {
  260.         return md5(serialize($criteria));
  261.     }
  262. }