feat: implement MySQL driver and schema discovery service for dynamic database management

This commit is contained in:
Ümit Tunç
2026-04-24 22:45:20 +03:00
parent 5af75c95dd
commit b5282df56f
5 changed files with 68 additions and 6 deletions
@@ -111,7 +111,9 @@ class SchemaController extends Controller
try {
$this->initializeDriver($request);
$config = $request->only(['host', 'username', 'password', 'database', 'port', 'table']);
$filePath = $this->databaseService->export($config);
$filters = json_decode($request->get('filters', '[]'), true);
$filePath = $this->databaseService->export($config, $filters);
return Response::download($filePath)->deleteFileAfterSend(true);
} catch (\Exception $e) {
+57 -1
View File
@@ -147,11 +147,16 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface
return $this->query($sql, [$table, $dbName]);
}
public function export(array $config): string
public function export(array $config, array $filters = []): string
{
$database = $config['database'] ?? '';
$table = $config['table'] ?? '';
// If filters are provided, we do a manual export for the filtered rows
if (!empty($filters) && !empty($table)) {
return $this->exportFilteredTable($database, $table, $filters);
}
$filename = !empty($table)
? "{$table}-" . date('Y-m-d') . ".sql"
: "backup-" . ($database ?: 'all') . "-" . date('Y-m-d') . ".sql";
@@ -392,4 +397,55 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface
throw $e;
}
}
protected function exportFilteredTable(string $database, string $table, array $filters): string
{
$query = DB::connection($this->connectionName)->table($table);
$this->applyFilters($query, $filters);
$rows = $query->get();
$filename = "{$table}-filtered-" . date('Y-m-d-His') . ".sql";
$directory = storage_path('app/backups');
if (!is_dir($directory)) {
mkdir($directory, 0755, true);
}
$path = $directory . DIRECTORY_SEPARATOR . $filename;
$handle = fopen($path, 'w');
fwrite($handle, "-- Mariavel Filtered SQL Export\n");
fwrite($handle, "-- Database: {$database}\n");
fwrite($handle, "-- Table: {$table}\n");
fwrite($handle, "-- Date: " . date('Y-m-d H:i:s') . "\n\n");
$createTableResults = $this->query("SHOW CREATE TABLE `{$table}`");
if (!empty($createTableResults)) {
$createTable = $createTableResults[0];
$createSql = $createTable->{'Create Table'} ?? '';
if ($createSql) {
fwrite($handle, "DROP TABLE IF EXISTS `{$table}`;\n");
fwrite($handle, $createSql . ";\n\n");
}
}
if ($rows->count() > 0) {
foreach ($rows as $row) {
$fields = [];
$values = [];
foreach ((array)$row as $field => $value) {
$fields[] = "`{$field}`";
if (is_null($value)) {
$values[] = "NULL";
} else {
$values[] = DB::connection($this->connectionName)->getPdo()->quote($value);
}
}
$sql = "INSERT INTO `{$table}` (" . implode(', ', $fields) . ") VALUES (" . implode(', ', $values) . ");\n";
fwrite($handle, $sql);
}
}
fclose($handle);
return $path;
}
}
+2 -2
View File
@@ -102,9 +102,9 @@ class DatabaseService
/**
* Export the database.
*/
public function export(array $config): string
public function export(array $config, array $filters = []): string
{
return $this->getDriver()->export($config);
return $this->getDriver()->export($config, $filters);
}
/**
+1 -1
View File
@@ -116,7 +116,7 @@ const MainContent: React.FC = () => {
// For now, we'll use the existing SQL export for 'sql'
// and implement a client-side CSV export for 'csv' since we have the rows
if (format === 'sql') {
const response = await SchemaService.exportDatabase(activeDatabase, activeTable);
const response = await SchemaService.exportDatabase(activeDatabase, activeTable, filterModel.items);
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
+5 -1
View File
@@ -36,7 +36,11 @@ export const SchemaService = {
bulkAction: (data: { tables: string[], action: string, database: string }) => api.post('/schema/bulk-action', data),
batchUpdate: (table: string, changes: any[]) => api.post(`/schema/${table}/batch-update`, { changes }),
executeQuery: (query: string) => api.post('/schema/execute', { query }),
exportDatabase: (database?: string, table?: string) => api.post('/schema/export', { database, table }, { responseType: 'blob' }),
exportDatabase: (database?: string, table?: string, filters?: any) => api.post('/schema/export', {
database,
table,
filters: filters ? JSON.stringify(filters) : undefined
}, { responseType: 'blob' }),
importDatabase: (formData: FormData) => api.post('/schema/import', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
}),