custom/plugins/SwagPlatformSecurity/src/Fixes/NEXT37527/SalesChannelRepository.php line 137

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\System\SalesChannel\Entity;
  3. use Shopware\Core\Content\Product\Aggregate\ProductVisibility\ProductVisibilityDefinition;
  4. use Shopware\Core\Content\Product\SalesChannel\ProductAvailableFilter;
  5. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductDefinition;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Entity;
  7. use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection;
  8. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityAggregationResultLoadedEvent;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEventFactory;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchResultLoadedEvent;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Read\EntityReaderInterface;
  16. use Shopware\Core\Framework\DataAbstractionLayer\RepositorySearchDetector;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntityAggregatorInterface;
  20. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearcherInterface;
  21. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
  22. use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
  23. use Shopware\Core\Framework\Feature;
  24. use Shopware\Core\Framework\Log\Package;
  25. use Shopware\Core\Framework\Struct\ArrayEntity;
  26. use Shopware\Core\System\SalesChannel\Event\SalesChannelProcessCriteriaEvent;
  27. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  28. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  29. /**
  30.  * @final tag:v6.5.0
  31.  */
  32. #[Package('sales-channel')]
  33. class SalesChannelRepository implements SalesChannelRepositoryInterface
  34. {
  35.     /**
  36.      * @var EntityDefinition
  37.      */
  38.     protected $definition;
  39.     /**
  40.      * @var EntityReaderInterface
  41.      */
  42.     protected $reader;
  43.     /**
  44.      * @var EntitySearcherInterface
  45.      */
  46.     protected $searcher;
  47.     /**
  48.      * @var EntityAggregatorInterface
  49.      */
  50.     protected $aggregator;
  51.     /**
  52.      * @var EventDispatcherInterface
  53.      */
  54.     protected $eventDispatcher;
  55.     private ?EntityLoadedEventFactory $eventFactory;
  56.     /**
  57.      * @internal
  58.      *
  59.      * @deprecated tag:v6.5.0 - $eventFactory parameter will be required
  60.      */
  61.     public function __construct(
  62.         EntityDefinition $definition,
  63.         EntityReaderInterface $reader,
  64.         EntitySearcherInterface $searcher,
  65.         EntityAggregatorInterface $aggregator,
  66.         EventDispatcherInterface $eventDispatcher,
  67.         ?EntityLoadedEventFactory $eventFactory
  68.     ) {
  69.         $this->definition $definition;
  70.         $this->reader $reader;
  71.         $this->searcher $searcher;
  72.         $this->aggregator $aggregator;
  73.         $this->eventDispatcher $eventDispatcher;
  74.         if ($eventFactory !== null) {
  75.             $this->eventFactory $eventFactory;
  76.         } else {
  77.             Feature::triggerDeprecationOrThrow(
  78.                 'v6.5.0.0',
  79.                 sprintf('SalesChannelRepository constructor for definition %s requires the event factory as required 6th parameter in v6.5.0.0'$definition->getEntityName())
  80.             );
  81.         }
  82.     }
  83.     /**
  84.      * @deprecated tag:v6.5.0 - Will be removed
  85.      */
  86.     public function setEntityLoadedEventFactory(EntityLoadedEventFactory $eventFactory): void
  87.     {
  88.         Feature::triggerDeprecationOrThrow(
  89.             'v6.5.0.0',
  90.             Feature::deprecatedMethodMessage(__CLASS____METHOD__'v6.5.0.0')
  91.         );
  92.         $this->eventFactory $eventFactory;
  93.     }
  94.     /**
  95.      * @throws InconsistentCriteriaIdsException
  96.      */
  97.     public function search(Criteria $criteriaSalesChannelContext $salesChannelContext): EntitySearchResult
  98.     {
  99.         $criteria = clone $criteria;
  100.         $this->processCriteria($criteria$salesChannelContext);
  101.         $aggregations null;
  102.         if ($criteria->getAggregations()) {
  103.             $aggregations $this->aggregate($criteria$salesChannelContext);
  104.         }
  105.         if (!RepositorySearchDetector::isSearchRequired($this->definition$criteria)) {
  106.             $entities $this->read($criteria$salesChannelContext);
  107.             return new EntitySearchResult($this->definition->getEntityName(), $entities->count(), $entities$aggregations$criteria$salesChannelContext->getContext());
  108.         }
  109.         $ids $this->doSearch($criteria$salesChannelContext);
  110.         if (empty($ids->getIds())) {
  111.             /** @var EntityCollection<Entity> $collection */
  112.             $collection $this->definition->getCollectionClass();
  113.             return new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), new $collection(), $aggregations$criteria$salesChannelContext->getContext());
  114.         }
  115.         $readCriteria $criteria->cloneForRead($ids->getIds());
  116.         $entities $this->read($readCriteria$salesChannelContext);
  117.         $search $ids->getData();
  118.         /** @var Entity $element */
  119.         foreach ($entities as $element) {
  120.             if (!\array_key_exists($element->getUniqueIdentifier(), $search)) {
  121.                 continue;
  122.             }
  123.             $data $search[$element->getUniqueIdentifier()];
  124.             unset($data['id']);
  125.             if (empty($data)) {
  126.                 continue;
  127.             }
  128.             $element->addExtension('search', new ArrayEntity($data));
  129.         }
  130.         $result = new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), $entities$aggregations$criteria$salesChannelContext->getContext());
  131.         $result->addState(...$ids->getStates());
  132.         $event = new EntitySearchResultLoadedEvent($this->definition$result);
  133.         $this->eventDispatcher->dispatch($event$event->getName());
  134.         $event = new SalesChannelEntitySearchResultLoadedEvent($this->definition$result$salesChannelContext);
  135.         $this->eventDispatcher->dispatch($event$event->getName());
  136.         return $result;
  137.     }
  138.     public function aggregate(Criteria $criteriaSalesChannelContext $salesChannelContext): AggregationResultCollection
  139.     {
  140.         $criteria = clone $criteria;
  141.         $this->processCriteria($criteria$salesChannelContext);
  142.         $result $this->aggregator->aggregate($this->definition$criteria$salesChannelContext->getContext());
  143.         $event = new EntityAggregationResultLoadedEvent($this->definition$result$salesChannelContext->getContext());
  144.         $this->eventDispatcher->dispatch($event$event->getName());
  145.         return $result;
  146.     }
  147.     public function searchIds(Criteria $criteriaSalesChannelContext $salesChannelContext): IdSearchResult
  148.     {
  149.         $criteria = clone $criteria;
  150.         $this->processCriteria($criteria$salesChannelContext);
  151.         return $this->doSearch($criteria$salesChannelContext);
  152.     }
  153.     /**
  154.      * @return EntityCollection<Entity>
  155.      */
  156.     private function read(Criteria $criteriaSalesChannelContext $salesChannelContext): EntityCollection
  157.     {
  158.         $criteria = clone $criteria;
  159.         $entities $this->reader->read($this->definition$criteria$salesChannelContext->getContext());
  160.         if ($this->eventFactory === null) {
  161.             throw new \RuntimeException('Event loaded factory was not injected');
  162.         }
  163.         if (method_exists($criteria'getFields')) {
  164.             if ($criteria->getFields() === []) {
  165.                 $events $this->eventFactory->createForSalesChannel($entities->getElements(), $salesChannelContext);
  166.             } else {
  167.                 $events $this->eventFactory->createPartialForSalesChannel($entities->getElements(), $salesChannelContext);
  168.             }
  169.         } else {
  170.             $events $this->eventFactory->createForSalesChannel($entities->getElements(), $salesChannelContext);
  171.         }
  172.         foreach ($events as $event) {
  173.             $this->eventDispatcher->dispatch($event);
  174.         }
  175.         return $entities;
  176.     }
  177.     private function doSearch(Criteria $criteriaSalesChannelContext $salesChannelContext): IdSearchResult
  178.     {
  179.         $result $this->searcher->search($this->definition$criteria$salesChannelContext->getContext());
  180.         $event = new SalesChannelEntityIdSearchResultLoadedEvent($this->definition$result$salesChannelContext);
  181.         $this->eventDispatcher->dispatch($event$event->getName());
  182.         return $result;
  183.     }
  184.     private function processCriteria(Criteria $topCriteriaSalesChannelContext $salesChannelContext): void
  185.     {
  186.         if (!$this->definition instanceof SalesChannelDefinitionInterface) {
  187.             return;
  188.         }
  189.         $queue = [
  190.             ['definition' => $this->definition'criteria' => $topCriteria'path' => ''],
  191.         ];
  192.         $maxCount 100;
  193.         $processed = [];
  194.         // process all associations breadth-first
  195.         while (!empty($queue) && --$maxCount 0) {
  196.             $cur array_shift($queue);
  197.             $definition $cur['definition'];
  198.             $criteria $cur['criteria'];
  199.             $path $cur['path'];
  200.             $processedKey $path get_class($definition);
  201.             if (isset($processed[$processedKey])) {
  202.                 continue;
  203.             }
  204.             if ($definition instanceof SalesChannelDefinitionInterface) {
  205.                 if ($definition instanceof SalesChannelProductDefinition) {
  206.                     if ($path === '') {
  207.                         $definition->processCriteria($criteria$salesChannelContext);
  208.                     } else if (!$this->hasAvailableFilter($criteria)) {
  209.                         $criteria->addFilter(
  210.                             new ProductAvailableFilter($salesChannelContext->getSalesChannel()->getId(), ProductVisibilityDefinition::VISIBILITY_LINK)
  211.                         );
  212.                     }
  213.                 } else {
  214.                     $definition->processCriteria($criteria$salesChannelContext);
  215.                 }
  216.                 $eventName \sprintf('sales_channel.%s.process.criteria'$definition->getEntityName());
  217.                 $event = new SalesChannelProcessCriteriaEvent($criteria$salesChannelContext);
  218.                 $this->eventDispatcher->dispatch($event$eventName);
  219.             }
  220.             $processed[$processedKey] = true;
  221.             foreach ($criteria->getAssociations() as $associationName => $associationCriteria) {
  222.                 // find definition
  223.                 $field $definition->getField($associationName);
  224.                 if (!$field instanceof AssociationField) {
  225.                     continue;
  226.                 }
  227.                 $referenceDefinition $field->getReferenceDefinition();
  228.                 $queue[] = ['definition' => $referenceDefinition'criteria' => $associationCriteria'path' => $path '.' $associationName];
  229.                 if (!$field instanceof ManyToManyAssociationField) {
  230.                     continue;
  231.                 }
  232.                 $referenceDefinition $field->getToManyReferenceDefinition();
  233.                 $queue[] = ['definition' => $referenceDefinition'criteria' => $associationCriteria'path' => $path '.' $associationName];
  234.             }
  235.         }
  236.     }
  237.     private function hasAvailableFilter(Criteria $criteria): bool
  238.     {
  239.         foreach ($criteria->getFilters() as $filter) {
  240.             if ($filter instanceof ProductAvailableFilter) {
  241.                 return true;
  242.             }
  243.         }
  244.         return false;
  245.     }
  246. }