vendor/shopware/core/Framework/DataAbstractionLayer/EntityRepository.php line 253

Open in your IDE?
  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\Feature;
  21. use Shopware\Core\Framework\Log\Package;
  22. use Shopware\Core\Framework\Struct\ArrayEntity;
  23. use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException;
  24. use Shopware\Core\Framework\Uuid\Uuid;
  25. use Shopware\Core\Profiling\Profiler;
  26. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  27. /**
  28. * @final tag:v6.5.0
  29. */
  30. #[Package('core')]
  31. class EntityRepository implements EntityRepositoryInterface
  32. {
  33. private EntityReaderInterface $reader;
  34. private EntitySearcherInterface $searcher;
  35. private EntityAggregatorInterface $aggregator;
  36. private EventDispatcherInterface $eventDispatcher;
  37. private VersionManager $versionManager;
  38. private EntityDefinition $definition;
  39. private ?EntityLoadedEventFactory $eventFactory = null;
  40. /**
  41. * @internal
  42. *
  43. * @deprecated tag:v6.5.0 - parameter $eventFactory will be required
  44. */
  45. public function __construct(
  46. EntityDefinition $definition,
  47. EntityReaderInterface $reader,
  48. VersionManager $versionManager,
  49. EntitySearcherInterface $searcher,
  50. EntityAggregatorInterface $aggregator,
  51. EventDispatcherInterface $eventDispatcher,
  52. ?EntityLoadedEventFactory $eventFactory = null
  53. ) {
  54. $this->reader = $reader;
  55. $this->searcher = $searcher;
  56. $this->aggregator = $aggregator;
  57. $this->eventDispatcher = $eventDispatcher;
  58. $this->versionManager = $versionManager;
  59. $this->definition = $definition;
  60. if ($eventFactory !== null) {
  61. $this->eventFactory = $eventFactory;
  62. } else {
  63. Feature::triggerDeprecationOrThrow(
  64. 'v6.5.0.0',
  65. sprintf('EntityRepository constructor for definition %s requires the event factory as required 7th parameter in v6.5.0.0', $definition->getEntityName())
  66. );
  67. }
  68. }
  69. /**
  70. * @deprecated tag:v6.5.0 - Will be removed, inject entity loaded event factory in __construct
  71. */
  72. public function setEntityLoadedEventFactory(EntityLoadedEventFactory $eventFactory): void
  73. {
  74. if (isset($this->eventFactory)) {
  75. return;
  76. }
  77. Feature::triggerDeprecationOrThrow(
  78. 'v6.5.0.0',
  79. sprintf('Repository for definition %s requires the event factory as __construct parameter', $this->definition->getEntityName())
  80. );
  81. $this->eventFactory = $eventFactory;
  82. }
  83. public function getDefinition(): EntityDefinition
  84. {
  85. return $this->definition;
  86. }
  87. public function search(Criteria $criteria, Context $context): EntitySearchResult
  88. {
  89. if (!$criteria->getTitle()) {
  90. return $this->_search($criteria, $context);
  91. }
  92. return Profiler::trace($criteria->getTitle(), function () use ($criteria, $context) {
  93. return $this->_search($criteria, $context);
  94. }, 'repository');
  95. }
  96. public function aggregate(Criteria $criteria, Context $context): AggregationResultCollection
  97. {
  98. $criteria = clone $criteria;
  99. $result = $this->aggregator->aggregate($this->definition, $criteria, $context);
  100. $event = new EntityAggregationResultLoadedEvent($this->definition, $result, $context);
  101. $this->eventDispatcher->dispatch($event, $event->getName());
  102. return $result;
  103. }
  104. public function searchIds(Criteria $criteria, Context $context): IdSearchResult
  105. {
  106. $criteria = clone $criteria;
  107. $this->eventDispatcher->dispatch(new EntitySearchedEvent($criteria, $this->definition, $context));
  108. $result = $this->searcher->search($this->definition, $criteria, $context);
  109. $event = new EntityIdSearchResultLoadedEvent($this->definition, $result);
  110. $this->eventDispatcher->dispatch($event, $event->getName());
  111. return $result;
  112. }
  113. /**
  114. * @param array<array<string, mixed|null>> $data
  115. */
  116. public function update(array $data, Context $context): EntityWrittenContainerEvent
  117. {
  118. ReplicaConnection::ensurePrimary();
  119. $affected = $this->versionManager->update($this->definition, $data, WriteContext::createFromContext($context));
  120. $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, []);
  121. $this->eventDispatcher->dispatch($event);
  122. return $event;
  123. }
  124. /**
  125. * @param array<array<string, mixed|null>> $data
  126. */
  127. public function upsert(array $data, Context $context): EntityWrittenContainerEvent
  128. {
  129. ReplicaConnection::ensurePrimary();
  130. $affected = $this->versionManager->upsert($this->definition, $data, WriteContext::createFromContext($context));
  131. $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, []);
  132. $this->eventDispatcher->dispatch($event);
  133. return $event;
  134. }
  135. /**
  136. * @param array<array<string, mixed|null>> $data
  137. */
  138. public function create(array $data, Context $context): EntityWrittenContainerEvent
  139. {
  140. ReplicaConnection::ensurePrimary();
  141. $affected = $this->versionManager->insert($this->definition, $data, WriteContext::createFromContext($context));
  142. $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, []);
  143. $this->eventDispatcher->dispatch($event);
  144. return $event;
  145. }
  146. /**
  147. * @param array<array<string, mixed|null>> $ids
  148. */
  149. public function delete(array $ids, Context $context): EntityWrittenContainerEvent
  150. {
  151. ReplicaConnection::ensurePrimary();
  152. $affected = $this->versionManager->delete($this->definition, $ids, WriteContext::createFromContext($context));
  153. $event = EntityWrittenContainerEvent::createWithDeletedEvents($affected->getDeleted(), $context, $affected->getNotFound());
  154. if ($affected->getWritten()) {
  155. $updates = EntityWrittenContainerEvent::createWithWrittenEvents($affected->getWritten(), $context, []);
  156. if ($updates->getEvents() !== null) {
  157. $event->addEvent(...$updates->getEvents());
  158. }
  159. }
  160. $this->eventDispatcher->dispatch($event);
  161. return $event;
  162. }
  163. public function createVersion(string $id, Context $context, ?string $name = null, ?string $versionId = null): string
  164. {
  165. ReplicaConnection::ensurePrimary();
  166. if (!$this->definition->isVersionAware()) {
  167. throw new \RuntimeException(sprintf('Entity %s is not version aware', $this->definition->getEntityName()));
  168. }
  169. return $this->versionManager->createVersion($this->definition, $id, WriteContext::createFromContext($context), $name, $versionId);
  170. }
  171. public function merge(string $versionId, Context $context): void
  172. {
  173. ReplicaConnection::ensurePrimary();
  174. if (!$this->definition->isVersionAware()) {
  175. throw new \RuntimeException(sprintf('Entity %s is not version aware', $this->definition->getEntityName()));
  176. }
  177. $this->versionManager->merge($versionId, WriteContext::createFromContext($context));
  178. }
  179. public function clone(string $id, Context $context, ?string $newId = null, ?CloneBehavior $behavior = null): EntityWrittenContainerEvent
  180. {
  181. ReplicaConnection::ensurePrimary();
  182. $newId = $newId ?? Uuid::randomHex();
  183. if (!Uuid::isValid($newId)) {
  184. throw new InvalidUuidException($newId);
  185. }
  186. $affected = $this->versionManager->clone(
  187. $this->definition,
  188. $id,
  189. $newId,
  190. $context->getVersionId(),
  191. WriteContext::createFromContext($context),
  192. $behavior ?? new CloneBehavior()
  193. );
  194. $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, [], true);
  195. $this->eventDispatcher->dispatch($event);
  196. return $event;
  197. }
  198. /**
  199. * @return EntityCollection<Entity>
  200. */
  201. private function read(Criteria $criteria, Context $context): EntityCollection
  202. {
  203. $criteria = clone $criteria;
  204. $entities = $this->reader->read($this->definition, $criteria, $context);
  205. if ($this->eventFactory === null) {
  206. throw new \RuntimeException('Event loaded factory was not injected');
  207. }
  208. if ($criteria->getFields() === []) {
  209. $event = $this->eventFactory->create($entities->getElements(), $context);
  210. } else {
  211. $event = $this->eventFactory->createPartial($entities->getElements(), $context);
  212. }
  213. $this->eventDispatcher->dispatch($event);
  214. return $entities;
  215. }
  216. private function _search(Criteria $criteria, Context $context): EntitySearchResult
  217. {
  218. $criteria = clone $criteria;
  219. $aggregations = null;
  220. if ($criteria->getAggregations()) {
  221. $aggregations = $this->aggregate($criteria, $context);
  222. }
  223. if (!RepositorySearchDetector::isSearchRequired($this->definition, $criteria)) {
  224. $this->eventDispatcher->dispatch(
  225. new EntitySearchedEvent($criteria, $this->definition, $context)
  226. );
  227. $entities = $this->read($criteria, $context);
  228. return new EntitySearchResult($this->definition->getEntityName(), $entities->count(), $entities, $aggregations, $criteria, $context);
  229. }
  230. $ids = $this->searchIds($criteria, $context);
  231. if (empty($ids->getIds())) {
  232. /** @var EntityCollection<Entity> $collection */
  233. $collection = $this->definition->getCollectionClass();
  234. return new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), new $collection(), $aggregations, $criteria, $context);
  235. }
  236. $readCriteria = $criteria->cloneForRead($ids->getIds());
  237. $entities = $this->read($readCriteria, $context);
  238. $search = $ids->getData();
  239. /** @var Entity $element */
  240. foreach ($entities as $element) {
  241. if (!\array_key_exists($element->getUniqueIdentifier(), $search)) {
  242. continue;
  243. }
  244. $data = $search[$element->getUniqueIdentifier()];
  245. unset($data['id']);
  246. if (empty($data)) {
  247. continue;
  248. }
  249. $element->addExtension('search', new ArrayEntity($data));
  250. }
  251. $result = new EntitySearchResult($this->definition->getEntityName(), $ids->getTotal(), $entities, $aggregations, $criteria, $context);
  252. $result->addState(...$ids->getStates());
  253. $event = new EntitySearchResultLoadedEvent($this->definition, $result);
  254. $this->eventDispatcher->dispatch($event, $event->getName());
  255. return $result;
  256. }
  257. }