> */ private $repositoryHash = []; /** @var array */ private $mappers = []; /** * @param AttributeExtractorFactory $attributeExtractorFactory * @throws RuntimeException */ public function __construct( private DatabaseParams $databaseParams, private Metadata $metadata, private RepositoryFactory $repositoryFactory, private EntityFactory $entityFactory, private QueryComposerFactory $queryComposerFactory, ValueFactoryFactory $valueFactoryFactory, AttributeExtractorFactory $attributeExtractorFactory, EventDispatcher $eventDispatcher, private PDOProvider $pdoProvider, private ?MapperFactory $mapperFactory = null, ?QueryExecutor $queryExecutor = null, ?SqlExecutor $sqlExecutor = null ) { 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 = $sqlExecutor ?? new DefaultSqlExecutor($this->pdoProvider); $this->queryExecutor = $queryExecutor ?? new DefaultQueryExecutor($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() ?? ''; $this->queryComposer = $this->queryComposerFactory->create($platform); } private function initLocker(): void { $platform = $this->databaseParams->getPlatform() ?? ''; $className = BaseLocker::class; if ($platform === 'Mysql') { $className = MysqlLocker::class; } $this->locker = new $className($this->pdoProvider->get(), $this->queryComposer, $this->transactionManager); } /** * Get the query composer. */ public function getQueryComposer(): QueryComposerWrapper { return new QueryComposerWrapper($this->queryComposer); } /** * Get the transaction manager. */ public function getTransactionManager(): TransactionManager { return $this->transactionManager; } /** * Get the locker. */ 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->metadata, $this->queryExecutor ); $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. */ public function saveEntity(Entity $entity, array $options = []): void { $entityType = $entity->getEntityType(); $this->getRepository($entityType)->save($entity, $options); } /** * 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->getEntityById($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; } /** * Get the entity factory. */ public function getEntityFactory(): EntityFactory { return $this->entityFactory; } /** * Get the collection factory. */ 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; } /** * @deprecated As of v7.0. Use `getCollectionFactory`. * @param array $data * @return EntityCollection */ public function createCollection(?string $entityType = null, array $data = []): EntityCollection { return $this->collectionFactory->create($entityType, $data); } /** * @deprecated As of v7.0. Use the Query Builder instead. Otherwise, code will be not portable. */ public function getPDO(): PDO { return $this->pdoProvider->get(); } /** * @todo Remove in v9.0. * @deprecated As of v6.0. Use `getQueryComposer`. */ public function getQuery(): QueryComposer { return $this->queryComposer; } }