Pairity/src/Database/DatabaseManager.php

262 lines
7 KiB
PHP
Raw Normal View History

2026-02-08 05:26:07 +00:00
<?php
declare(strict_types=1);
namespace Pairity\Database;
use Pairity\Contracts\Database\ConnectionInterface;
use Pairity\Contracts\Database\DatabaseManagerInterface;
use Pairity\Contracts\Database\DriverInterface;
use Pairity\Database\Drivers\MySQLDriver;
use Pairity\Database\Drivers\OracleDriver;
use Pairity\Database\Drivers\PostgresDriver;
use Pairity\Database\Drivers\SQLiteDriver;
use Pairity\Database\Drivers\SqlServerDriver;
use Pairity\Exceptions\PairityException;
use RuntimeException;
/**
* Class DatabaseManager
*
* Manages database connections and their lifecycles.
*
* @package Pairity\Database
*/
class DatabaseManager implements DatabaseManagerInterface
{
/**
* @var array<string, ConnectionInterface>
*/
protected array $connections = [];
/**
* @var array<string, DriverInterface>
*/
protected array $drivers = [];
/**
* @var \Pairity\Contracts\Container\ContainerInterface|null
*/
protected ?\Pairity\Contracts\Container\ContainerInterface $container = null;
/**
* @var UnitOfWork|null
*/
protected ?UnitOfWork $unitOfWork = null;
/**
* @var \Pairity\Contracts\Events\DispatcherInterface|null
*/
protected ?\Pairity\Contracts\Events\DispatcherInterface $dispatcher = null;
/**
* DatabaseManager constructor.
*
* @param array $config The database configuration.
* @param \Pairity\Contracts\Container\ContainerInterface|null $container
*/
public function __construct(
protected array $config,
?\Pairity\Contracts\Container\ContainerInterface $container = null
) {
$this->container = $container;
}
/**
* @inheritDoc
*/
public function getDispatcher(): \Pairity\Contracts\Events\DispatcherInterface
{
if (!$this->dispatcher) {
$this->dispatcher = new \Pairity\Events\Dispatcher();
}
return $this->dispatcher;
}
/**
* @inheritDoc
*/
public function setDispatcher(\Pairity\Contracts\Events\DispatcherInterface $dispatcher): void
{
$this->dispatcher = $dispatcher;
}
/**
* Get the Unit of Work instance.
*
* @return UnitOfWork
*/
public function unitOfWork(): UnitOfWork
{
if (!$this->unitOfWork) {
$this->unitOfWork = new UnitOfWork($this);
}
return $this->unitOfWork;
}
/**
* Set the container instance.
*
* @param \Pairity\Contracts\Container\ContainerInterface $container
* @return void
*/
public function setContainer(\Pairity\Contracts\Container\ContainerInterface $container): void
{
$this->container = $container;
}
/**
* @inheritDoc
*/
public function getContainer(): \Pairity\Contracts\Container\ContainerInterface
{
if (!$this->container) {
$translator = new \Pairity\Translation\Translator(__DIR__ . '/../Translations');
throw new RuntimeException($translator->trans('error.container_not_set'));
}
return $this->container;
}
/**
* @inheritDoc
*/
public function connection(?string $name = null): ConnectionInterface
{
$name = $name ?: $this->getDefaultConnection();
if (!isset($this->connections[$name])) {
$this->connections[$name] = $this->makeConnection($name);
}
return $this->connections[$name];
}
/**
* @inheritDoc
*/
public function reconnect(?string $name = null): ConnectionInterface
{
$name = $name ?: $this->getDefaultConnection();
$this->disconnect($name);
return $this->connection($name);
}
/**
* @inheritDoc
*/
public function disconnect(?string $name = null): void
{
$name = $name ?: $this->getDefaultConnection();
if (isset($this->connections[$name])) {
$this->connections[$name]->disconnect();
unset($this->connections[$name]);
}
}
/**
* @inheritDoc
*/
public function getDefaultConnection(): string
{
return $this->config['default'] ?? 'default';
}
/**
* @inheritDoc
*/
public function setDefaultConnection(string $name): void
{
$this->config['default'] = $name;
}
/**
* Resolve a connection instance.
*
* @param string $name
* @return ConnectionInterface
* @throws RuntimeException
*/
protected function makeConnection(string $name): ConnectionInterface
{
$config = $this->getConnectionConfig($name);
$driverName = $config['driver'] ?? 'sqlite';
$driver = $this->resolveDriver($driverName);
return new Connection($name, $driver, $config);
}
/**
* Get the configuration for a connection.
*
* @param string $name
* @return array
* @throws RuntimeException
*/
protected function getConnectionConfig(string $name): array
{
$connections = $this->config['connections'] ?? [];
if (!isset($connections[$name])) {
throw new RuntimeException("Database connection [{$name}] not configured.");
}
return $connections[$name];
}
/**
* Resolve the driver instance.
*
* @param string $name
* @return DriverInterface
* @throws RuntimeException
*/
protected function resolveDriver(string $name): DriverInterface
{
if (isset($this->drivers[$name])) {
return $this->drivers[$name];
}
$driver = match ($name) {
'sqlite' => new SQLiteDriver(),
'mysql' => new MySQLDriver(),
'pgsql', 'postgres' => new PostgresDriver(),
'sqlsrv', 'sqlserver' => new SqlServerDriver(),
'oci', 'oracle' => new OracleDriver(),
default => throw new RuntimeException("Database driver [{$name}] not supported."),
};
return $this->drivers[$name] = $driver;
}
/**
* Get the query grammar for a driver.
*
* @param string $driver
* @return \Pairity\Database\Query\Grammar
*/
public function getQueryGrammar(string $driver): \Pairity\Database\Query\Grammar
{
return match ($driver) {
'mysql' => new \Pairity\Database\Query\Grammars\MySqlGrammar(),
'pgsql', 'postgres' => new \Pairity\Database\Query\Grammars\PostgresGrammar(),
'sqlsrv', 'sqlserver' => new \Pairity\Database\Query\Grammars\SqlServerGrammar(),
'oci', 'oracle' => new \Pairity\Database\Query\Grammars\OracleGrammar(),
'sqlite' => new \Pairity\Database\Query\Grammars\SqliteGrammar(),
default => (function() use ($driver) {
$translator = $this->getContainer()->get(\Pairity\Contracts\Translation\TranslatorInterface::class);
throw new \Pairity\Exceptions\DatabaseException(
$translator->trans('error.driver_not_supported', ['driver' => $driver]),
0,
null,
['driver' => $driver]
);
})(),
};
}
}