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

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