src/Core/Framework/DataAbstractionLayer/EntityRepository.php line 47

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\DataAbstractionLayer;
  3. use Shopware\Core\Framework\Adapter\Database\ReplicaConnection;
  4. use Shopware\Core\Framework\Context;
  5. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityAggregationResultLoadedEvent;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityIdSearchResultLoadedEvent;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEventFactory;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchedEvent;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchResultLoadedEvent;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Read\EntityReaderInterface;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntityAggregatorInterface;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearcherInterface;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
  17. use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;
  18. use Shopware\Core\Framework\DataAbstractionLayer\Write\CloneBehavior;
  19. use Shopware\Core\Framework\DataAbstractionLayer\Write\WriteContext;
  20. use Shopware\Core\Framework\Log\Package;
  21. use Shopware\Core\Framework\Struct\ArrayEntity;
  22. use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException;
  23. use Shopware\Core\Framework\Uuid\Uuid;
  24. use Shopware\Core\Profiling\Profiler;
  25. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  26. /**
  27.  * @final
  28.  */
  29. #[Package('core')]
  30. class EntityRepository
  31. {
  32.     /**
  33.      * @internal
  34.      */
  35.     public function __construct(private readonly EntityDefinition $definition, private readonly EntityReaderInterface $reader, private readonly VersionManager $versionManager, private readonly EntitySearcherInterface $searcher, private readonly EntityAggregatorInterface $aggregator, private readonly EventDispatcherInterface $eventDispatcher, private readonly EntityLoadedEventFactory $eventFactory)
  36.     {
  37.     }
  38.     public function getDefinition(): EntityDefinition
  39.     {
  40.         return $this->definition;
  41.     }
  42.     public function search(Criteria $criteriaContext $context): EntitySearchResult
  43.     {
  44.         if (!$criteria->getTitle()) {
  45.             return $this->_search($criteria$context);
  46.         }
  47.         return Profiler::trace($criteria->getTitle(), fn () => $this->_search($criteria$context), 'repository');
  48.     }
  49.     public function aggregate(Criteria $criteriaContext $context): AggregationResultCollection
  50.     {
  51.         $criteria = clone $criteria;
  52.         $result $this->aggregator->aggregate($this->definition$criteria$context);
  53.         $event = new EntityAggregationResultLoadedEvent($this->definition$result$context);
  54.         $this->eventDispatcher->dispatch($event$event->getName());
  55.         return $result;
  56.     }
  57.     public function searchIds(Criteria $criteriaContext $context): IdSearchResult
  58.     {
  59.         $criteria = clone $criteria;
  60.         $this->eventDispatcher->dispatch(new EntitySearchedEvent($criteria$this->definition$context));
  61.         $result $this->searcher->search($this->definition$criteria$context);
  62.         $event = new EntityIdSearchResultLoadedEvent($this->definition$result);
  63.         $this->eventDispatcher->dispatch($event$event->getName());
  64.         return $result;
  65.     }
  66.     /**
  67.      * @param array<array<string, mixed|null>> $data
  68.      */
  69.     public function update(array $dataContext $context): EntityWrittenContainerEvent
  70.     {
  71.         ReplicaConnection::ensurePrimary();
  72.         $affected $this->versionManager->update($this->definition$dataWriteContext::createFromContext($context));
  73.         $event EntityWrittenContainerEvent::createWithWrittenEvents($affected$context, []);
  74.         $this->eventDispatcher->dispatch($event);
  75.         return $event;
  76.     }
  77.     /**
  78.      * @param array<array<string, mixed|null>> $data
  79.      */
  80.     public function upsert(array $dataContext $context): EntityWrittenContainerEvent
  81.     {
  82.         ReplicaConnection::ensurePrimary();
  83.         $affected $this->versionManager->upsert($this->definition$dataWriteContext::createFromContext($context));
  84.         $event EntityWrittenContainerEvent::createWithWrittenEvents($affected$context, []);
  85.         $this->eventDispatcher->dispatch($event);
  86.         return $event;
  87.     }
  88.     /**
  89.      * @param array<array<string, mixed|null>> $data
  90.      */
  91.     public function create(array $dataContext $context): EntityWrittenContainerEvent
  92.     {
  93.         ReplicaConnection::ensurePrimary();
  94.         $affected $this->versionManager->insert($this->definition$dataWriteContext::createFromContext($context));
  95.         $event EntityWrittenContainerEvent::createWithWrittenEvents($affected$context, []);
  96.         $this->eventDispatcher->dispatch($event);
  97.         return $event;
  98.     }
  99.     /**
  100.      * @param array<array<string, mixed|null>> $ids
  101.      */
  102.     public function delete(array $idsContext $context): EntityWrittenContainerEvent
  103.     {
  104.         ReplicaConnection::ensurePrimary();
  105.         $affected $this->versionManager->delete($this->definition$idsWriteContext::createFromContext($context));
  106.         $event EntityWrittenContainerEvent::createWithDeletedEvents($affected->getDeleted(), $context$affected->getNotFound());
  107.         if ($affected->getWritten()) {
  108.             $updates EntityWrittenContainerEvent::createWithWrittenEvents($affected->getWritten(), $context, []);
  109.             if ($updates->getEvents() !== null) {
  110.                 $event->addEvent(...$updates->getEvents());
  111.             }
  112.         }
  113.         $this->eventDispatcher->dispatch($event);
  114.         return $event;
  115.     }
  116.     public function createVersion(string $idContext $context, ?string $name null, ?string $versionId null): string
  117.     {
  118.         ReplicaConnection::ensurePrimary();
  119.         if (!$this->definition->isVersionAware()) {
  120.             throw new \RuntimeException(sprintf('Entity %s is not version aware'$this->definition->getEntityName()));
  121.         }
  122.         return $this->versionManager->createVersion($this->definition$idWriteContext::createFromContext($context), $name$versionId);
  123.     }
  124.     public function merge(string $versionIdContext $context): void
  125.     {
  126.         ReplicaConnection::ensurePrimary();
  127.         if (!$this->definition->isVersionAware()) {
  128.             throw new \RuntimeException(sprintf('Entity %s is not version aware'$this->definition->getEntityName()));
  129.         }
  130.         $this->versionManager->merge($versionIdWriteContext::createFromContext($context));
  131.     }
  132.     public function clone(string $idContext $context, ?string $newId null, ?CloneBehavior $behavior null): EntityWrittenContainerEvent
  133.     {
  134.         ReplicaConnection::ensurePrimary();
  135.         $newId ??= Uuid::randomHex();
  136.         if (!Uuid::isValid($newId)) {
  137.             throw new InvalidUuidException($newId);
  138.         }
  139.         $affected $this->versionManager->clone(
  140.             $this->definition,
  141.             $id,
  142.             $newId,
  143.             $context->getVersionId(),
  144.             WriteContext::createFromContext($context),
  145.             $behavior ?? new CloneBehavior()
  146.         );
  147.         $event EntityWrittenContainerEvent::createWithWrittenEvents($affected$context, [], true);
  148.         $this->eventDispatcher->dispatch($event);
  149.         return $event;
  150.     }
  151.     /**
  152.      * @return EntityCollection<Entity>
  153.      */
  154.     private function read(Criteria $criteriaContext $context): EntityCollection
  155.     {
  156.         $criteria = clone $criteria;
  157.         $entities $this->reader->read($this->definition$criteria$context);
  158.         if ($criteria->getFields() === []) {
  159.             $event $this->eventFactory->create($entities->getElements(), $context);
  160.         } else {
  161.             $event $this->eventFactory->createPartial($entities->getElements(), $context);
  162.         }
  163.         $this->eventDispatcher->dispatch($event);
  164.         return $entities;
  165.     }
  166.     private function _search(Criteria $criteriaContext $context): EntitySearchResult
  167.     {
  168.         $criteria = clone $criteria;
  169.         $aggregations null;
  170.         if ($criteria->getAggregations()) {
  171.             $aggregations $this->aggregate($criteria$context);
  172.         }
  173.         if (!RepositorySearchDetector::isSearchRequired($this->definition$criteria)) {
  174.             $this->eventDispatcher->dispatch(
  175.                 new EntitySearchedEvent($criteria$this->definition$context)
  176.             );
  177.             $entities $this->read($criteria$context);
  178.             return new EntitySearchResult($this->definition->getEntityName(), $entities->count(), $entities$aggregations$criteria$context);
  179.         }
  180.         $ids $this->searchIds($criteria$context);
  181.         if (empty($ids->getIds())) {
  182.             /** @var EntityCollection<Entity> $collection */
  183.             $collection $this->definition->getCollectionClass();
  184.             return new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), new $collection(), $aggregations$criteria$context);
  185.         }
  186.         $readCriteria $criteria->cloneForRead($ids->getIds());
  187.         $entities $this->read($readCriteria$context);
  188.         $search $ids->getData();
  189.         /** @var Entity $element */
  190.         foreach ($entities as $element) {
  191.             if (!\array_key_exists($element->getUniqueIdentifier(), $search)) {
  192.                 continue;
  193.             }
  194.             $data $search[$element->getUniqueIdentifier()];
  195.             unset($data['id']);
  196.             if (empty($data)) {
  197.                 continue;
  198.             }
  199.             $element->addExtension('search', new ArrayEntity($data));
  200.         }
  201.         $result = new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), $entities$aggregations$criteria$context);
  202.         $result->addState(...$ids->getStates());
  203.         $event = new EntitySearchResultLoadedEvent($this->definition$result);
  204.         $this->eventDispatcher->dispatch($event$event->getName());
  205.         return $result;
  206.     }
  207. }