<?php declare(strict_types=1);namespace Shopware\Core\Framework\DataAbstractionLayer;use Shopware\Core\Framework\Adapter\Database\ReplicaConnection;use Shopware\Core\Framework\Context;use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityAggregationResultLoadedEvent;use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityIdSearchResultLoadedEvent;use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityLoadedEventFactory;use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchedEvent;use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchResultLoadedEvent;use Shopware\Core\Framework\DataAbstractionLayer\Event\EntityWrittenContainerEvent;use Shopware\Core\Framework\DataAbstractionLayer\Read\EntityReaderInterface;use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection;use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;use Shopware\Core\Framework\DataAbstractionLayer\Search\EntityAggregatorInterface;use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearcherInterface;use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult;use Shopware\Core\Framework\DataAbstractionLayer\Write\CloneBehavior;use Shopware\Core\Framework\DataAbstractionLayer\Write\WriteContext;use Shopware\Core\Framework\Feature;use Shopware\Core\Framework\Log\Package;use Shopware\Core\Framework\Struct\ArrayEntity;use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException;use Shopware\Core\Framework\Uuid\Uuid;use Shopware\Core\Profiling\Profiler;use Symfony\Component\EventDispatcher\EventDispatcherInterface;/** * @final tag:v6.5.0 */#[Package('core')]class EntityRepository implements EntityRepositoryInterface{ private EntityReaderInterface $reader; private EntitySearcherInterface $searcher; private EntityAggregatorInterface $aggregator; private EventDispatcherInterface $eventDispatcher; private VersionManager $versionManager; private EntityDefinition $definition; private ?EntityLoadedEventFactory $eventFactory = null; /** * @internal * * @deprecated tag:v6.5.0 - parameter $eventFactory will be required */ public function __construct( EntityDefinition $definition, EntityReaderInterface $reader, VersionManager $versionManager, EntitySearcherInterface $searcher, EntityAggregatorInterface $aggregator, EventDispatcherInterface $eventDispatcher, ?EntityLoadedEventFactory $eventFactory = null ) { $this->reader = $reader; $this->searcher = $searcher; $this->aggregator = $aggregator; $this->eventDispatcher = $eventDispatcher; $this->versionManager = $versionManager; $this->definition = $definition; if ($eventFactory !== null) { $this->eventFactory = $eventFactory; } else { Feature::triggerDeprecationOrThrow( 'v6.5.0.0', sprintf('EntityRepository constructor for definition %s requires the event factory as required 7th parameter in v6.5.0.0', $definition->getEntityName()) ); } } /** * @deprecated tag:v6.5.0 - Will be removed, inject entity loaded event factory in __construct */ public function setEntityLoadedEventFactory(EntityLoadedEventFactory $eventFactory): void { if (isset($this->eventFactory)) { return; } Feature::triggerDeprecationOrThrow( 'v6.5.0.0', sprintf('Repository for definition %s requires the event factory as __construct parameter', $this->definition->getEntityName()) ); $this->eventFactory = $eventFactory; } public function getDefinition(): EntityDefinition { return $this->definition; } public function search(Criteria $criteria, Context $context): EntitySearchResult { if (!$criteria->getTitle()) { return $this->_search($criteria, $context); } return Profiler::trace($criteria->getTitle(), function () use ($criteria, $context) { return $this->_search($criteria, $context); }, 'repository'); } public function aggregate(Criteria $criteria, Context $context): AggregationResultCollection { $criteria = clone $criteria; $result = $this->aggregator->aggregate($this->definition, $criteria, $context); $event = new EntityAggregationResultLoadedEvent($this->definition, $result, $context); $this->eventDispatcher->dispatch($event, $event->getName()); return $result; } public function searchIds(Criteria $criteria, Context $context): IdSearchResult { $criteria = clone $criteria; $this->eventDispatcher->dispatch(new EntitySearchedEvent($criteria, $this->definition, $context)); $result = $this->searcher->search($this->definition, $criteria, $context); $event = new EntityIdSearchResultLoadedEvent($this->definition, $result); $this->eventDispatcher->dispatch($event, $event->getName()); return $result; } /** * @param array<array<string, mixed|null>> $data */ public function update(array $data, Context $context): EntityWrittenContainerEvent { ReplicaConnection::ensurePrimary(); $affected = $this->versionManager->update($this->definition, $data, WriteContext::createFromContext($context)); $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, []); $this->eventDispatcher->dispatch($event); return $event; } /** * @param array<array<string, mixed|null>> $data */ public function upsert(array $data, Context $context): EntityWrittenContainerEvent { ReplicaConnection::ensurePrimary(); $affected = $this->versionManager->upsert($this->definition, $data, WriteContext::createFromContext($context)); $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, []); $this->eventDispatcher->dispatch($event); return $event; } /** * @param array<array<string, mixed|null>> $data */ public function create(array $data, Context $context): EntityWrittenContainerEvent { ReplicaConnection::ensurePrimary(); $affected = $this->versionManager->insert($this->definition, $data, WriteContext::createFromContext($context)); $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, []); $this->eventDispatcher->dispatch($event); return $event; } /** * @param array<array<string, mixed|null>> $ids */ public function delete(array $ids, Context $context): EntityWrittenContainerEvent { ReplicaConnection::ensurePrimary(); $affected = $this->versionManager->delete($this->definition, $ids, WriteContext::createFromContext($context)); $event = EntityWrittenContainerEvent::createWithDeletedEvents($affected->getDeleted(), $context, $affected->getNotFound()); if ($affected->getWritten()) { $updates = EntityWrittenContainerEvent::createWithWrittenEvents($affected->getWritten(), $context, []); if ($updates->getEvents() !== null) { $event->addEvent(...$updates->getEvents()); } } $this->eventDispatcher->dispatch($event); return $event; } public function createVersion(string $id, Context $context, ?string $name = null, ?string $versionId = null): string { ReplicaConnection::ensurePrimary(); if (!$this->definition->isVersionAware()) { throw new \RuntimeException(sprintf('Entity %s is not version aware', $this->definition->getEntityName())); } return $this->versionManager->createVersion($this->definition, $id, WriteContext::createFromContext($context), $name, $versionId); } public function merge(string $versionId, Context $context): void { ReplicaConnection::ensurePrimary(); if (!$this->definition->isVersionAware()) { throw new \RuntimeException(sprintf('Entity %s is not version aware', $this->definition->getEntityName())); } $this->versionManager->merge($versionId, WriteContext::createFromContext($context)); } public function clone(string $id, Context $context, ?string $newId = null, ?CloneBehavior $behavior = null): EntityWrittenContainerEvent { ReplicaConnection::ensurePrimary(); $newId = $newId ?? Uuid::randomHex(); if (!Uuid::isValid($newId)) { throw new InvalidUuidException($newId); } $affected = $this->versionManager->clone( $this->definition, $id, $newId, $context->getVersionId(), WriteContext::createFromContext($context), $behavior ?? new CloneBehavior() ); $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, [], true); $this->eventDispatcher->dispatch($event); return $event; } /** * @return EntityCollection<Entity> */ private function read(Criteria $criteria, Context $context): EntityCollection { $criteria = clone $criteria; $entities = $this->reader->read($this->definition, $criteria, $context); if ($this->eventFactory === null) { throw new \RuntimeException('Event loaded factory was not injected'); } if ($criteria->getFields() === []) { $event = $this->eventFactory->create($entities->getElements(), $context); } else { $event = $this->eventFactory->createPartial($entities->getElements(), $context); } $this->eventDispatcher->dispatch($event); return $entities; } private function _search(Criteria $criteria, Context $context): EntitySearchResult { $criteria = clone $criteria; $aggregations = null; if ($criteria->getAggregations()) { $aggregations = $this->aggregate($criteria, $context); } if (!RepositorySearchDetector::isSearchRequired($this->definition, $criteria)) { $this->eventDispatcher->dispatch( new EntitySearchedEvent($criteria, $this->definition, $context) ); $entities = $this->read($criteria, $context); return new EntitySearchResult($this->definition->getEntityName(), $entities->count(), $entities, $aggregations, $criteria, $context); } $ids = $this->searchIds($criteria, $context); if (empty($ids->getIds())) { /** @var EntityCollection<Entity> $collection */ $collection = $this->definition->getCollectionClass(); return new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), new $collection(), $aggregations, $criteria, $context); } $readCriteria = $criteria->cloneForRead($ids->getIds()); $entities = $this->read($readCriteria, $context); $search = $ids->getData(); /** @var Entity $element */ foreach ($entities as $element) { if (!\array_key_exists($element->getUniqueIdentifier(), $search)) { continue; } $data = $search[$element->getUniqueIdentifier()]; unset($data['id']); if (empty($data)) { continue; } $element->addExtension('search', new ArrayEntity($data)); } $result = new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), $entities, $aggregations, $criteria, $context); $result->addState(...$ids->getStates()); $event = new EntitySearchResultLoadedEvent($this->definition, $result); $this->eventDispatcher->dispatch($event, $event->getName()); return $result; }}