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

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