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 {
|
try {
|
||||||
$this->initializeDriver($request);
|
$this->initializeDriver($request);
|
||||||
$config = $request->only(['host', 'username', 'password', 'database', 'port', 'table']);
|
$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);
|
return Response::download($filePath)->deleteFileAfterSend(true);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|||||||
@@ -147,11 +147,16 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface
|
|||||||
return $this->query($sql, [$table, $dbName]);
|
return $this->query($sql, [$table, $dbName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function export(array $config): string
|
public function export(array $config, array $filters = []): string
|
||||||
{
|
{
|
||||||
$database = $config['database'] ?? '';
|
$database = $config['database'] ?? '';
|
||||||
$table = $config['table'] ?? '';
|
$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)
|
$filename = !empty($table)
|
||||||
? "{$table}-" . date('Y-m-d') . ".sql"
|
? "{$table}-" . date('Y-m-d') . ".sql"
|
||||||
: "backup-" . ($database ?: 'all') . "-" . date('Y-m-d') . ".sql";
|
: "backup-" . ($database ?: 'all') . "-" . date('Y-m-d') . ".sql";
|
||||||
@@ -392,4 +397,55 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface
|
|||||||
throw $e;
|
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.
|
* 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'
|
// 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
|
// and implement a client-side CSV export for 'csv' since we have the rows
|
||||||
if (format === 'sql') {
|
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 url = window.URL.createObjectURL(new Blob([response.data]));
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
link.href = url;
|
link.href = url;
|
||||||
|
|||||||
@@ -36,7 +36,11 @@ export const SchemaService = {
|
|||||||
bulkAction: (data: { tables: string[], action: string, database: string }) => api.post('/schema/bulk-action', data),
|
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 }),
|
batchUpdate: (table: string, changes: any[]) => api.post(`/schema/${table}/batch-update`, { changes }),
|
||||||
executeQuery: (query: string) => api.post('/schema/execute', { query }),
|
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, {
|
importDatabase: (formData: FormData) => api.post('/schema/import', formData, {
|
||||||
headers: { 'Content-Type': 'multipart/form-data' }
|
headers: { 'Content-Type': 'multipart/form-data' }
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user