connection = $connection; return $clone; } public function listDatabases(string $sort, string $dir, string $search = ''): array { try { $rows = collect(DB::connection($this->connection)->select(self::QUERY)) ->map(fn ($row) => (array) $row); if ($search !== '') { $needle = mb_strtolower($search); $rows = $rows->filter(fn (array $row) => str_contains(mb_strtolower($row['db_name'] ?? ''), $needle)); } $rows = $rows->sortBy(fn (array $row) => $row[$sort] ?? '', SORT_REGULAR, $dir === 'desc'); return [ 'rows' => $rows->values()->all(), 'meta' => [ 'total' => $rows->count(), 'server' => config('database.connections.' . $this->connection . '.host'), 'driver' => 'mysql', 'sql_started' => null, 'uptime_days' => null, ], 'error' => null, ]; } catch (Throwable) { return [ 'rows' => [], 'meta' => [ 'total' => 0, 'server' => config('database.connections.' . $this->connection . '.host'), 'driver' => 'mysql', 'sql_started' => null, 'uptime_days' => null, ], 'error' => 'Unable to load MySQL/MariaDB databases. Check connectivity and credentials.', ]; } } public function makeOffline(string $databaseName): void { $databaseName = $this->validateName($databaseName); if ($this->isProtected($databaseName)) { throw new InvalidArgumentException('System databases cannot be restricted.'); } $quoted = '`' . str_replace('`', '``', $databaseName) . '`'; DB::connection($this->connection)->statement("ALTER DATABASE {$quoted} READ ONLY = 1"); } public function isProtected(string $databaseName): bool { return in_array(mb_strtolower($databaseName), self::PROTECTED, true); } public function supportsOffline(): bool { return true; } private function validateName(string $databaseName): string { $databaseName = trim($databaseName); if (! preg_match('/^[a-zA-Z0-9_\-]+$/', $databaseName)) { throw new InvalidArgumentException('Invalid database name.'); } return $databaseName; } }