> */ private $repositoryHash = []; /** @var array */ private $mappers = []; /** * @param AttributeExtractorFactory $attributeExtractorFactory * @throws RuntimeException */ public function __construct( DatabaseParams $databaseParams, Metadata $metadata, RepositoryFactory $repositoryFactory, EntityFactory $entityFactory, QueryComposerFactory $queryComposerFactory, ValueFactoryFactory $valueFactoryFactory, AttributeExtractorFactory $attributeExtractorFactory, EventDispatcher $eventDispatcher, PDOProvider $pdoProvider, ?MapperFactory $mapperFactory = null ) { $this->databaseParams = $databaseParams; $this->metadata = $metadata; $this->queryComposerFactory = $queryComposerFactory; $this->entityFactory = $entityFactory; $this->repositoryFactory = $repositoryFactory; $this->pdoProvider = $pdoProvider; $this->mapperFactory = $mapperFactory; if (!$this->databaseParams->getPlatform()) { throw new RuntimeException("No 'platform' parameter."); } $valueAccessorFactory = new ValueAccessorFactory( $valueFactoryFactory, $attributeExtractorFactory, $eventDispatcher ); $this->entityFactory->setEntityManager($this); $this->entityFactory->setValueAccessorFactory($valueAccessorFactory); $this->initQueryComposer(); $this->sqlExecutor = new SqlExecutor($this->pdoProvider->get()); $this->queryExecutor = new QueryExecutor($this->sqlExecutor, $this->getQueryComposer()); $this->queryBuilder = new QueryBuilder(); $this->collectionFactory = new CollectionFactory($this); $this->transactionManager = new TransactionManager($this->pdoProvider->get(), $this->queryComposer); $this->initLocker(); } private function initQueryComposer(): void { $platform = $this->databaseParams->getPlatform() ?? 'Dummy'; $this->queryComposer = $this->queryComposerFactory->create($platform); } private function initLocker(): void { $platform = $this->databaseParams->getPlatform() ?? 'Dummy'; $className = BaseLocker::class; if ($platform === 'Mysql') { $className = MysqlLocker::class; } $locker = new $className($this->pdoProvider->get(), $this->queryComposer, $this->transactionManager); $this->locker = $locker; } /** * @todo Remove in v7.4. * @deprecated */ public function getQuery(): QueryComposer { return $this->queryComposer; } public function getQueryComposer(): QueryComposerWrapper { return new QueryComposerWrapper($this->queryComposer); } public function getTransactionManager(): TransactionManager { return $this->transactionManager; } public function getLocker(): Locker { return $this->locker; } /** * Get a Mapper. */ public function getMapper(string $name = self::RDB_MAPPER_NAME): Mapper { if (!array_key_exists($name, $this->mappers)) { $this->loadMapper($name); } return $this->mappers[$name]; } private function loadMapper(string $name): void { if ($name === self::RDB_MAPPER_NAME) { $mapper = new BaseMapper( $this->pdoProvider->get(), $this->entityFactory, $this->collectionFactory, $this->queryComposer, $this->metadata, $this->sqlExecutor ); $this->mappers[$name] = $mapper; return; } if (!$this->mapperFactory) { throw new RuntimeException("Could not create mapper '{$name}'. No mapper factory."); } $this->mappers[$name] = $this->mapperFactory->create($name); } /** * Get an entity. If $id is null, a new entity instance is created. * If an entity with a specified ID does not exist, then NULL is returned. */ public function getEntity(string $entityType, ?string $id = null): ?Entity { if (!$this->hasRepository($entityType)) { throw new RuntimeException("ORM: Repository '{$entityType}' does not exist."); } if ($id === null) { return $this->getRepository($entityType)->getNew(); } return $this->getRepository($entityType)->getById($id); } /** * Create a new entity instance (w/o storing to DB). */ public function getNewEntity(string $entityType): Entity { /** @var Entity */ return $this->getEntity($entityType); } /** * Get an entity by ID. If an entity does not exist, NULL is returned. */ public function getEntityById(string $entityType, string $id): ?Entity { return $this->getEntity($entityType, $id); } /** * Store an entity. * * @param array $options Options. * @return void * * @todo Change return type to void in v7.4. */ public function saveEntity(Entity $entity, array $options = []) { $entityType = $entity->getEntityType(); $this->getRepository($entityType)->save($entity, $options); /** @phpstan-ignore-next-line */ return $entity->getId(); } /** * Mark an entity as deleted (in database). * * @param array $options Options. */ public function removeEntity(Entity $entity, array $options = []): void { $entityType = $entity->getEntityType(); $this->getRepository($entityType)->remove($entity, $options); } /** * Refresh an entity from the database, overwriting made changes, if any. * Can be used to fetch attributes that were not fetched initially. * * @throws RuntimeException */ public function refreshEntity(Entity $entity): void { if ($entity->isNew()) { throw new RuntimeException("Can't refresh a new entity."); } if (!$entity->hasId()) { throw new RuntimeException("Can't refresh an entity w/o ID."); } $fetchedEntity = $this->getEntity($entity->getEntityType(), $entity->getId()); if (!$fetchedEntity) { throw new RuntimeException("Can't refresh a non-existent entity."); } $entity->set($fetchedEntity->getValueMap()); $entity->setAsFetched(); } /** * Create entity (and store to database). * * @param stdClass|array $data Entity attributes. * @param array $options Options. */ public function createEntity(string $entityType, $data = [], array $options = []): Entity { $entity = $this->getNewEntity($entityType); $entity->set($data); $this->saveEntity($entity, $options); return $entity; } /** * Check whether a repository for a specific entity type exist. */ public function hasRepository(string $entityType): bool { return $this->getMetadata()->has($entityType); } /** * Get a repository for a specific entity type. * * @return Repository */ public function getRepository(string $entityType): Repository { if (!$this->hasRepository($entityType)) { throw new RuntimeException("Repository '{$entityType}' does not exist."); } if (!array_key_exists($entityType, $this->repositoryHash)) { $this->repositoryHash[$entityType] = $this->repositoryFactory->create($entityType); } return $this->repositoryHash[$entityType]; } /** * Get an RDB repository for a specific entity type. * * @return RDBRepository */ public function getRDBRepository(string $entityType): RDBRepository { $repository = $this->getRepository($entityType); if (!$repository instanceof RDBRepository) { throw new RuntimeException("Repository '{$entityType}' is not RDB."); } return $repository; } /** * Get an RDB repository by an entity class name. * * @template T of Entity * @param class-string $className An entity class name. * @return RDBRepository */ public function getRDBRepositoryByClass(string $className): RDBRepository { $entityType = RepositoryUtil::getEntityTypeByClass($className); /** @var RDBRepository */ return $this->getRDBRepository($entityType); } /** * Get a repository by an entity class name. * * @template T of Entity * @param class-string $className An entity class name. * @return Repository */ public function getRepositoryByClass(string $className): Repository { $entityType = RepositoryUtil::getEntityTypeByClass($className); /** @var Repository */ return $this->getRepository($entityType); } /** * Get metadata definitions. */ public function getDefs(): Defs { return $this->metadata->getDefs(); } /** * Get a query builder. */ public function getQueryBuilder(): QueryBuilder { return $this->queryBuilder; } /** * Get metadata. */ public function getMetadata(): Metadata { return $this->metadata; } /** * @deprecated */ public function getPDO(): PDO { return $this->pdoProvider->get(); } /** * @deprecated Use `getCollectionFactory`. * @param array $data * * @return EntityCollection */ public function createCollection(?string $entityType = null, array $data = []): EntityCollection { return $this->collectionFactory->create($entityType, $data); } public function getEntityFactory(): EntityFactory { return $this->entityFactory; } public function getCollectionFactory(): CollectionFactory { return $this->collectionFactory; } /** * Get a Query Executor. */ public function getQueryExecutor(): QueryExecutor { return $this->queryExecutor; } /** * Get SQL Executor. */ public function getSqlExecutor(): SqlExecutor { return $this->sqlExecutor; } }