'Mysql', 'mysqli' => 'Mysql', ]; public function __construct(array $params, RepositoryFactory $repositoryFactory, EntityFactory $entityFactory) { $this->params = $params; $this->metadata = new Metadata(); if (empty($this->params['platform'])) { if (empty($this->params['driver'])) { throw new \Exception('No database driver specified.'); } $driver = $this->params['driver']; if (empty($this->driverPlatformMap[$driver])) { throw new \Exception("Database driver '{$driver}' is not supported."); } $this->params['platform'] = $this->driverPlatformMap[$this->params['driver']]; } if (!empty($params['metadata'])) { $this->setMetadata($params['metadata']); } $this->entityFactory = $entityFactory; $this->entityFactory->setEntityManager($this); $this->repositoryFactory = $repositoryFactory; } /** * Get Query. */ public function getQuery() : Query { if (empty($this->query)) { $platform = $this->params['platform']; $className = 'Espo\\ORM\\DB\\Query\\' . ucfirst($platform) . 'Query'; $this->query = new $className($this->getPDO(), $this->entityFactory, $this->metadata); } return $this->query; } protected function getMapperClassName(string $name) { $className = null; switch ($name) { case 'RDB': $platform = $this->params['platform']; $className = 'Espo\\ORM\\DB\\' . ucfirst($platform) . 'Mapper'; break; } if (!$className || !class_exists($className)) { throw new Error("Mapper {$name} does not exist."); } return $className; } /** * Get Mapper. */ public function getMapper(?string $name = null) : Mapper { $name = $name ?? $this->defaultMapperName; $className = $this->getMapperClassName($name); if (empty($this->mappers[$className])) { $this->mappers[$className] = new $className( $this->getPDO(), $this->entityFactory, $this->getQuery(), $this->metadata ); } return $this->mappers[$className]; } protected function initPDO() { $params = $this->params; $port = empty($params['port']) ? '' : 'port=' . $params['port'] . ';'; $platform = strtolower($params['platform']); $options = []; if (isset($params['sslCA'])) { $options[PDO::MYSQL_ATTR_SSL_CA] = $params['sslCA']; } if (isset($params['sslCert'])) { $options[PDO::MYSQL_ATTR_SSL_CERT] = $params['sslCert']; } if (isset($params['sslKey'])) { $options[PDO::MYSQL_ATTR_SSL_KEY] = $params['sslKey']; } if (isset($params['sslCAPath'])) { $options[PDO::MYSQL_ATTR_SSL_CAPATH] = $params['sslCAPath']; } if (isset($params['sslCipher'])) { $options[PDO::MYSQL_ATTR_SSL_CIPHER] = $params['sslCipher']; } $this->pdo = new PDO( $platform . ':host='.$params['host'].';'.$port.'dbname=' . $params['dbname'] . ';charset=' . $params['charset'], $params['user'], $params['password'], $options ); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } /** * Get entity. If $id is null, a new entity instance is created. * If 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 Error("ORM: Repository '{$entityType}' does not exist."); } return $this->getRepository($entityType)->get($id); } /** * Store entity (in database). */ public function saveEntity(Entity $entity, array $options = []) { $entityType = $entity->getEntityType(); $this->getRepository($entityType)->save($entity, $options); } /** * Mark entity as deleted (in database). */ public function removeEntity(Entity $entity, array $options = []) { $entityType = $entity->getEntityType(); $this->getRepository($entityType)->remove($entity, $options); } /** * Create entity (store it in database). * * @param \StdClass|array $data Entity attributes. */ public function createEntity(string $entityType, $data, array $options = []) : Entity { $entity = $this->getEntity($entityType); $entity->set($data); $this->saveEntity($entity, $options); return $entity; } /** * Fetch entity (from database). */ public function fetchEntity(string $entityType, string $id) : ?Entity { if (empty($id)) return null; return $this->getEntity($entityType, $id); } /** * 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. */ public function getRepository(string $entityType) : ?Repository { if (!$this->hasRepository($entityType)) { // TODO Throw error } if (empty($this->repositoryHash[$entityType])) { $this->repositoryHash[$entityType] = $this->repositoryFactory->create($entityType); } return $this->repositoryHash[$entityType] ?? null; } /** * Create a select builder. */ public function createSelectBuilder() : RDBSelectBuilder { return new RDBSelectBuilder($this); } public function setMetadata(array $data) { $this->metadata->setData($data); } public function getMetadata() { return $this->metadata; } /** * Get an instance of PDO. */ public function getPDO() : \PDO { if (empty($this->pdo)) { $this->initPDO(); } return $this->pdo; } /** * Create a Collection. * Entity type can be omitted. */ public function createCollection(?string $entityType = null, array $data = []) { $collection = new EntityCollection($data, $entityType, $this->entityFactory); return $collection; } /** * Create an Sth Collection. Sth collection is preferable when a select query returns a large number of rows. */ public function createSthCollection(string $entityType, array $selectParams = []) { return new SthCollection($entityType, $this, $selectParams); } public function getEntityFactory() : object { return $this->entityFactory; } /** * Run a query. Returns a result. * * @param $rerunIfDeadlock Query will be re-run if a deadlock occurs. */ public function runQuery(string $query, bool $rerunIfDeadlock = false) { try { return $this->getPDO()->query($query); } catch (\Exception $e) { if ($rerunIfDeadlock) { if ($e->errorInfo[0] == 40001 && $e->errorInfo[1] == 1213) { return $this->getPDO()->query($query); } else { throw $e; } } } } }