diff --git a/app/Models/NexusModel.php b/app/Models/NexusModel.php index 3786dd73..3341fb7d 100644 --- a/app/Models/NexusModel.php +++ b/app/Models/NexusModel.php @@ -15,7 +15,10 @@ class NexusModel extends Model protected $perPage = 50; - protected $connection = NexusDB::ELOQUENT_CONNECTION_NAME; + public function getConnectionName() + { + return NexusDB::getConnectionName(); + } protected function usernameForAdmin(): Attribute { diff --git a/app/Models/User.php b/app/Models/User.php index f9000cea..88e19655 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -31,8 +31,6 @@ class User extends Authenticatable implements FilamentUser, HasName protected $perPage = 50; - protected $connection = NexusDB::ELOQUENT_CONNECTION_NAME; - const STATUS_CONFIRMED = 'confirmed'; const STATUS_PENDING = 'pending'; @@ -109,6 +107,11 @@ class User extends Authenticatable implements FilamentUser, HasName private const USER_ENABLE_LATELY = "user_enable_lately:%s"; + public function getConnectionName() + { + return NexusDB::getConnectionName(); + } + public static function getUserEnableLatelyCacheKey(int $userId): string { return sprintf(self::USER_ENABLE_LATELY, $userId); diff --git a/config/nexus.php b/config/nexus.php index ebf4f951..957fc93e 100644 --- a/config/nexus.php +++ b/config/nexus.php @@ -8,6 +8,45 @@ return [ 'log_split' => nexus_env('LOG_SPLIT', 'daily'), + 'database' => [ + 'default' => nexus_env('DB_CONNECTION', 'mysql'), + 'connections' => [ + 'mysql' => [ + 'driver' => 'mysql', + 'url' => nexus_env('DATABASE_URL'), + 'host' => nexus_env('DB_HOST', '127.0.0.1'), + 'port' => (int)nexus_env('DB_PORT', 3306), + 'username' => nexus_env('DB_USERNAME', 'root'), + 'password' => nexus_env('DB_PASSWORD', ''), + 'database' => nexus_env('DB_DATABASE', 'nexusphp'), + 'unix_socket' => nexus_env('DB_SOCKET', ''), + 'charset' => 'utf8mb4', + 'collation' => 'utf8mb4_unicode_ci', + 'prefix' => '', + 'prefix_indexes' => true, + 'strict' => false, + 'engine' => null, + 'options' => extension_loaded('pdo_mysql') ? array_filter([ + PDO::MYSQL_ATTR_SSL_CA => nexus_env('MYSQL_ATTR_SSL_CA'), + ]) : [], + ], + 'pgsql' => [ + 'driver' => 'pgsql', + 'url' => nexus_env('DATABASE_URL'), + 'host' => nexus_env('DB_HOST', '127.0.0.1'), + 'port' => nexus_env('DB_PORT', '5432'), + 'database' => nexus_env('DB_DATABASE', 'nexusphp'), + 'username' => nexus_env('DB_USERNAME', 'nexusphp'), + 'password' => nexus_env('DB_PASSWORD', ''), + 'charset' => 'utf8', + 'prefix' => '', + 'prefix_indexes' => true, + 'schema' => 'public', + 'sslmode' => 'prefer', + ], + ], + ], + 'mysql' => [ 'driver' => 'mysql', 'url' => nexus_env('DATABASE_URL'), diff --git a/include/globalfunctions.php b/include/globalfunctions.php index 7bde7f10..40efd758 100644 --- a/include/globalfunctions.php +++ b/include/globalfunctions.php @@ -622,10 +622,11 @@ function last_query($all = false, $format = 'json') { static $connection; if (is_null($connection)) { + $connectionName = \Nexus\Database\NexusDB::getConnectionName(); if (IN_NEXUS) { - $connection = \Illuminate\Database\Capsule\Manager::connection(\Nexus\Database\NexusDB::ELOQUENT_CONNECTION_NAME); + $connection = \Illuminate\Database\Capsule\Manager::connection($connectionName); } else { - $connection = \Illuminate\Support\Facades\DB::connection(config('database.default')); + $connection = \Illuminate\Support\Facades\DB::connection($connectionName); } } if ($all === 'COUNT') { diff --git a/nexus/Database/DBMysqli.php b/nexus/Database/DBMysqli.php index c1d6dc56..3e32dbee 100644 --- a/nexus/Database/DBMysqli.php +++ b/nexus/Database/DBMysqli.php @@ -1,6 +1,11 @@ driver = nexus_config('nexus.database.default'); + + if ($driver === 'mysql') { + $dsn = "mysql:host={$host};port={$port};dbname={$database};charset=utf8mb4"; + } elseif ($driver === 'pgsql') { + $dsn = "pgsql:host={$host};port={$port};dbname={$database}"; + } else { + throw new DatabaseException("Unsupported driver"); + } + + $pdo = new PDO($dsn, $username, $password, [ + PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 替代 mysqli 报错机制 + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, + ]); + + // ===== MySQL 专属 ===== + if ($driver === 'mysql') { + $pdo->exec("SET NAMES utf8mb4"); + $pdo->exec("SET collation_connection = 'utf8mb4_unicode_ci'"); + $pdo->exec("SET sql_mode=''"); + $pdo->exec("SET time_zone='".date('P')."'"); + } + + // ===== PostgreSQL 专属 ===== + if ($driver === 'pgsql') { + $pdo->exec("SET TIME ZONE '".date('P')."'"); + } + + return $this->pdo = $pdo; + } + + public function query(string $sql) + { + $this->lastStmt = $this->pdo->query($sql); + return $this->lastStmt; + } + + public function error(): string + { + $error = $this->pdo->errorInfo(); + return $error[2] ?? ''; + } + + public function errno(): int + { + $error = $this->pdo->errorInfo(); + return (int)($error[1] ?? 0); + } + + public function numRows($stmt): int + { + // ⚠️ PDO 对 SELECT 不可靠,这里兼容处理 + return $stmt->rowCount(); + } + + public function selectDb($database) + { + if ($this->driver === 'mysql') { + return $this->pdo->exec("USE `{$database}`"); + } + + if ($this->driver === 'pgsql') { + // PostgreSQL 不能切数据库,只能重连 + throw new DatabaseException("PostgreSQL does not support selectDb()"); + } + + return false; + } + + public function fetchAssoc($stmt) + { + return $stmt->fetch(PDO::FETCH_ASSOC); + } + + public function fetchRow($stmt) + { + return $stmt->fetch(PDO::FETCH_NUM); + } + + public function fetchArray($stmt, $type = null) + { + if ($type === null) { + return $stmt->fetch(PDO::FETCH_BOTH); + } + + // 兼容 mysqli 常量 + switch ($type) { + case MYSQLI_ASSOC: + return $stmt->fetch(PDO::FETCH_ASSOC); + case MYSQLI_NUM: + return $stmt->fetch(PDO::FETCH_NUM); + default: + return $stmt->fetch(PDO::FETCH_BOTH); + } + } + + public function affectedRows(): int + { + return $this->lastStmt ? $this->lastStmt->rowCount() : 0; + } + + public function escapeString(string $string): string + { + return substr($this->pdo->quote($string), 1, -1); + } + + public function lastInsertId(): int + { + return (int)$this->pdo->lastInsertId(); + } + + public function freeResult($stmt) + { + return $stmt->closeCursor(); + } + +} diff --git a/nexus/Database/NexusDB.php b/nexus/Database/NexusDB.php index 05ec8600..9477a097 100644 --- a/nexus/Database/NexusDB.php +++ b/nexus/Database/NexusDB.php @@ -57,7 +57,8 @@ class NexusDB return self::$instance; } $instance = new self; - $driver = new DBMysqli(); +// $driver = new DBMysqli(); + $driver = new DBPdo(); $instance->setDriver($driver); return self::$instance = $instance; } @@ -259,7 +260,7 @@ class NexusDB public static function bootEloquent(array $config) { $capsule = new Capsule(Container::getInstance()); - $connectionName = self::ELOQUENT_CONNECTION_NAME; + $connectionName = self::getConnectionName(); $capsule->addConnection($config, $connectionName); $capsule->setAsGlobal(); $capsule->bootEloquent(); @@ -271,7 +272,7 @@ class NexusDB private static function schema(): \Illuminate\Database\Schema\Builder { if (IN_NEXUS) { - return Capsule::schema(self::ELOQUENT_CONNECTION_NAME); + return Capsule::schema(self::getConnectionName()); } throw new \RuntimeException('can not call this when not in nexus.'); } @@ -295,7 +296,7 @@ class NexusDB public static function table($table): \Illuminate\Database\Query\Builder { if (IN_NEXUS) { - return Capsule::table($table, null, self::ELOQUENT_CONNECTION_NAME); + return Capsule::table($table, null, self::getConnectionName()); } return DB::table($table); } @@ -319,7 +320,7 @@ class NexusDB public static function transaction(\Closure $callback, $attempts = 1) { if (IN_NEXUS) { - return Capsule::connection(self::ELOQUENT_CONNECTION_NAME)->transaction($callback, $attempts); + return Capsule::connection(self::getConnectionName())->transaction($callback, $attempts); } return DB::transaction($callback, $attempts); } @@ -455,5 +456,11 @@ class NexusDB } } + public static function getConnectionName() + { + return nexus_config('nexus.database.default'); + } + + }