feat: implement MySQL driver and API schema services for database management

This commit is contained in:
Ümit Tunç
2026-04-24 08:29:43 +03:00
parent c433630e33
commit 60cd2fe051
10 changed files with 668 additions and 154 deletions
@@ -93,4 +93,120 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface
$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';
// 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
$passwordPart = !empty($password) ? "-p" . escapeshellarg($password) : "";
$dbPart = !empty($database) ? escapeshellarg($database) : "--all-databases";
// On Windows, we might need to handle double quotes in escapeshellarg
// Let's use a more robust way to execute and capture errors
$command = sprintf(
'mysqldump -u %s %s -h %s -P %s %s > %s 2> %s',
escapeshellarg($username),
$passwordPart,
escapeshellarg($host),
escapeshellarg($port),
$dbPart,
escapeshellarg($path),
escapeshellarg($errorPath)
);
shell_exec($command);
if (file_exists($errorPath)) {
$errors = file_get_contents($errorPath);
unlink($errorPath);
if (!empty(trim($errors))) {
// If the file is 0 bytes and there are errors, it definitely failed
if (!file_exists($path) || filesize($path) === 0) {
throw new \Exception("Export failed: " . $errors);
}
}
}
if (!file_exists($path) || filesize($path) === 0) {
throw new \Exception("Export failed: Resulting file is empty. Ensure mysqldump is installed and in the system PATH.");
}
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';
$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(
'mysql -u %s %s -h %s -P %s %s < %s 2> %s',
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))) {
throw new \Exception("Import failed: " . $errors);
}
}
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] ?? []);
}
}