connectionName}", [ 'driver' => 'mysql', 'host' => $config['host'] ?? '127.0.0.1', 'port' => $config['port'] ?? '3306', 'database' => $config['database'] ?? null, 'username' => $config['username'] ?? 'root', 'password' => $config['password'] ?? '', 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => false, 'engine' => null, ]); try { DB::purge($this->connectionName); DB::connection($this->connectionName)->getPdo(); return true; } catch (\Exception $e) { return false; } } public function query(string $sql, array $bindings = []): array { return DB::connection($this->connectionName)->select($sql, $bindings); } public function getConnection() { return DB::connection($this->connectionName); } public function getDatabases(): array { $results = $this->query('SHOW DATABASES'); return array_map(fn($db) => $db->Database, $results); } public function getTables(): array { $results = $this->query('SHOW TABLES'); $key = "Tables_in_" . DB::connection($this->connectionName)->getDatabaseName(); return array_map(fn($table) => $table->$key, $results); } public function getTableSchema(string $table): array { return $this->query("DESCRIBE `{$table}`"); } public function getTableData(string $table, int $limit = 100, int $offset = 0): array { return $this->query("SELECT * FROM `{$table}` LIMIT ? OFFSET ?", [$limit, $offset]); } public function getTableCount(string $table): int { $result = $this->query("SELECT COUNT(*) as count FROM `{$table}`"); return (int) ($result[0]->count ?? 0); } public function getForeignKeys(string $table): array { $sql = " SELECT COLUMN_NAME, REFERENCED_TABLE_NAME, REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME = ? AND REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_SCHEMA = ? "; $dbName = DB::connection($this->connectionName)->getDatabaseName(); return $this->query($sql, [$table, $dbName]); } public function export(array $config): string { $filename = 'backup-' . ($config['database'] ?? 'all') . '-' . date('Y-m-d-H-i-s') . '.sql'; $directory = storage_path('app/backups'); if (!is_dir($directory)) { mkdir($directory, 0755, true); } $path = $directory . DIRECTORY_SEPARATOR . $filename; $errorPath = $directory . DIRECTORY_SEPARATOR . $filename . '.err'; $mysqldumpPath = env('MYSQLDUMP_PATH', 'mysqldump'); // Ensure we have a username $username = $config['username'] ?? 'root'; $password = $config['password'] ?? ''; $host = $config['host'] ?? '127.0.0.1'; $port = $config['port'] ?? '3306'; $database = $config['database'] ?? ''; // Build command with flags for a complete and resilient export $passwordPart = !empty($password) ? "-p" . escapeshellarg($password) : ""; $dbPart = !empty($database) ? escapeshellarg($database) : "--all-databases"; // --single-transaction: for InnoDB tables, ensures consistency without locking // --skip-lock-tables: avoids issues with views/definers that might prevent locking // --routines, --triggers, --events: ensures all database objects are included // --quick: useful for large tables $flags = "--single-transaction --skip-lock-tables --routines --triggers --events --quick"; $command = sprintf( '%s -u %s %s -h %s -P %s %s %s > %s 2> %s', $mysqldumpPath === 'mysqldump' ? 'mysqldump' : escapeshellarg($mysqldumpPath), escapeshellarg($username), $passwordPart, escapeshellarg($host), escapeshellarg($port), $flags, $dbPart, escapeshellarg($path), escapeshellarg($errorPath) ); shell_exec($command); if (file_exists($errorPath)) { $errors = file_get_contents($errorPath); unlink($errorPath); // Some errors are fatal even if some output was produced (like the definer issue) if (!empty(trim($errors)) && (str_contains(strtolower($errors), 'error') || !file_exists($path) || filesize($path) < 1000)) { // If it's just a warning (like SSL), we might want to continue, // but if it's a "Got error", we should probably fail. if (str_contains(strtolower($errors), 'error')) { throw new \Exception("Export failed: " . $errors); } } } if (!file_exists($path) || filesize($path) === 0) { $instruction = "The 'mysqldump' binary was not found. Please set MYSQLDUMP_PATH in your .env file to the absolute path of the mysqldump executable."; throw new \Exception("Export failed: Binary not found.\n\nInstructions: " . $instruction); } return $path; } public function import(array $config, string $filePath): bool { $directory = storage_path('app/backups'); if (!is_dir($directory)) { mkdir($directory, 0755, true); } $errorPath = $directory . DIRECTORY_SEPARATOR . 'import_error.err'; $mysqlPath = env('MYSQL_BINARY_PATH', 'mysql'); $username = $config['username'] ?? 'root'; $password = $config['password'] ?? ''; $host = $config['host'] ?? '127.0.0.1'; $port = $config['port'] ?? '3306'; $database = $config['database'] ?? ''; $passwordPart = !empty($password) ? "-p" . escapeshellarg($password) : ""; $dbPart = !empty($database) ? escapeshellarg($database) : ""; $command = sprintf( '%s -u %s %s -h %s -P %s %s < %s 2> %s', $mysqlPath === 'mysql' ? 'mysql' : escapeshellarg($mysqlPath), escapeshellarg($username), $passwordPart, escapeshellarg($host), escapeshellarg($port), $dbPart, escapeshellarg($filePath), escapeshellarg($errorPath) ); shell_exec($command); if (file_exists($errorPath)) { $errors = file_get_contents($errorPath); unlink($errorPath); if (!empty(trim($errors))) { $instruction = "Please check your .env file and ensure MYSQL_BINARY_PATH is correct. Example: MYSQL_BINARY_PATH=C:\\xampp\\mysql\\bin\\mysql.exe"; throw new \Exception("Import failed: " . $errors . "\n\nInstructions: " . $instruction); } } return true; } public function getDatabaseMetadata(string $database): array { $sql = " SELECT s.DEFAULT_CHARACTER_SET_NAME as charset, s.DEFAULT_COLLATION_NAME as collation, (SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = s.SCHEMA_NAME) as size_bytes, (SELECT COUNT(*) FROM information_schema.TABLES WHERE table_schema = s.SCHEMA_NAME) as table_count, (SELECT COUNT(*) FROM information_schema.VIEWS WHERE table_schema = s.SCHEMA_NAME) as view_count FROM information_schema.SCHEMATA s WHERE s.SCHEMA_NAME = ? "; $results = $this->query($sql, [$database]); return (array) ($results[0] ?? []); } }