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

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