feat: implement MySQL driver and schema discovery service for dynamic database management
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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' }
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user