Skip to content

Commit c6db1d0

Browse files
committed
[ADD] Implemented automatic detection of foreign key dependency handling for database backup.
1 parent e836aa7 commit c6db1d0

1 file changed

Lines changed: 84 additions & 7 deletions

File tree

core/src/Support/MysqlDumper.php

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* @copyright Dennis Mozes
1212
* @license GNU/LGPL License: http://www.gnu.org/copyleft/lgpl.html
1313
*
14-
* Modified by Raymond for use with this module
14+
* Modified by Raymond and Seiger for use with this module
1515
*
1616
**/
1717
class MysqlDumper implements MysqlDumperInterface
@@ -82,14 +82,15 @@ public function createDump($callBack)
8282
$databaseName = $dataBaseConfig['database'];
8383

8484
$sql = 'SELECT table_name AS "table", round(((data_length + index_length) / 1024 / 1024)) "size" FROM information_schema.TABLES WHERE table_schema = "'.$databaseName.'"';
85-
$tableSizes = array_column($modx->db->makeArray($modx->db->query($sql)),'size','table');
85+
$tableSizes = array_column($modx->db->makeArray($modx->db->query($sql)), 'size', 'table');
86+
87+
// Sort tables by foreign key dependencies
88+
$tables = $this->sortTablesByDependencies($this->_dbtables);
8689

8790
// Set line feed
8891
$lf = "\n";
8992
$tempfile_path = MODX_BASE_PATH . 'assets/backup/temp.php';
9093

91-
$result = $modx->getDatabase()->query('SHOW TABLES');
92-
$tables = $this->result2Array(0, $result);
9394
foreach ($tables as $tblval) {
9495
$result = $modx->getDatabase()->query("SHOW CREATE TABLE `{$tblval}`");
9596
$createtable[$tblval] = $this->result2Array(1, $result);
@@ -122,8 +123,6 @@ public function createDump($callBack)
122123

123124

124125
foreach ($tables as $tblval) {
125-
126-
127126
// check for selected table
128127
if (isset($this->_dbtables)) {
129128
if (strstr(",{$this->_dbtables},", ",{$tblval},") === false) {
@@ -175,7 +174,7 @@ public function createDump($callBack)
175174

176175

177176
while ($arr = $modx->getDatabase()->getRow($result)) {
178-
//формируем блок значений
177+
//формируем блок значений
179178
$insertdump = "(";
180179
if (!is_array($arr)) $arr = array();
181180

@@ -309,4 +308,82 @@ public function setSnapshotFile($file){
309308
$this->snapshootFile = $file;
310309
}
311310

311+
/**
312+
* Sorts the tables based on their foreign key dependencies.
313+
*
314+
* This method sorts the given list of tables in a way that ensures tables with no dependencies (or primary tables)
315+
* are processed first, followed by tables that depend on other tables through foreign keys.
316+
* It uses a topological sorting approach to ensure that tables with dependencies are created in the correct order.
317+
* The method relies on the **getForeignKeyDependencies()** method to retrieve the dependencies of each table.
318+
*
319+
* @param array $dbtables An array of table names to be sorted based on their dependencies.
320+
*
321+
* @return array The sorted array of table names, with dependent tables placed after the ones they rely on.
322+
*/
323+
public function sortTablesByDependencies($dbtables)
324+
{
325+
$sorted = [];
326+
$visited = [];
327+
$dependencies = $this->getForeignKeyDependencies($dbtables);
328+
329+
foreach ($dbtables as $table) {
330+
$this->visitTable($table, $dependencies, $visited, $sorted);
331+
}
332+
333+
return $sorted;
334+
}
335+
336+
/**
337+
* Retrieves all foreign key dependencies for the given tables.
338+
* Returns an associative array with tables and their dependencies.
339+
*/
340+
public function getForeignKeyDependencies($tables)
341+
{
342+
$dependencies = [];
343+
344+
foreach ($tables as $table) {
345+
$sql = "SHOW CREATE TABLE `$table`";
346+
$result = evo()->db->query($sql);
347+
$createTableQuery = evo()->db->getRow($result)['Create Table'];
348+
preg_match_all('/FOREIGN KEY \(`([^`]+)`\) REFERENCES `([^`]+)` \(`([^`]+)`\)/', $createTableQuery, $matches);
349+
350+
if (!empty($matches[2])) {
351+
foreach ($matches[2] as $index => $referencedTable) {
352+
$dependencies[$table][] = $referencedTable;
353+
}
354+
}
355+
}
356+
357+
return $dependencies;
358+
}
359+
360+
/**
361+
* Checks the table and its dependencies, visits all the tables it depends on, and adds them to the sorted list.
362+
*
363+
* This method recursively visits tables, checking their dependencies, and adds them to the **$sorted** array.
364+
* If a table has already been visited, it is skipped.
365+
* It works based on the principle of topological sorting to ensure the correct order of table creation
366+
* during the dump process, taking foreign keys into account.
367+
*
368+
* @param string $table The name of the table to check.
369+
* @param array $dependencies An associative array containing tables and their dependencies.
370+
* @param array $visited An array containing already visited tables to avoid circular references.
371+
* @param array $sorted An array where tables are added after visiting their dependencies.
372+
*/
373+
private function visitTable($table, $dependencies, &$visited, &$sorted)
374+
{
375+
if (isset($visited[$table])) {
376+
return;
377+
}
378+
379+
$visited[$table] = true;
380+
381+
if (isset($dependencies[$table])) {
382+
foreach ($dependencies[$table] as $dependentTable) {
383+
$this->visitTable($dependentTable, $dependencies, $visited, $sorted);
384+
}
385+
}
386+
387+
$sorted[] = $table;
388+
}
312389
}

0 commit comments

Comments
 (0)