<?php
$data = [
[
'table' => 'test',
'parent_table' => NULL,
],
[
'table' => 'test',
'parent_table' => NULL,
],
[
'table' => 'test2',
'parent_table' => 'test',
],
[
'table' => 'test4',
'parent_table' => NULL,
],
[
'table' => 'test5',
'parent_table' => 'test3',
],
[
'table' => 'test6',
'parent_table' => 'test5',
],
[
'table' => 'test3',
'parent_table' => 'test',
],
];
function reorderHierarchy($data){
$hierarchy = [];
$top_level_parents = [];
foreach($data as $each_data){
$hierarchy[$each_data['table']] = array();
if(is_null($each_data['parent_table'])){
if(!isset($top_level_parents[$each_data['table']])){
$top_level_parents[$each_data['table']] = 0;
}
$top_level_parents[$each_data['table']]++;
}
}
foreach($data as $each_data){
if(!is_null($each_data['parent_table'])){
$hierarchy[$each_data['parent_table']][] = $each_data['table'];
}
}
$result = [];
traverseHierarchy($hierarchy,$top_level_parents,$result);
return $result;
}
function traverseHierarchy($hierarchy,$top_level_parents,&$result){
foreach($top_level_parents as $each_parent => $occurrences){
while($occurrences-- > 0){
$result[] = [
'table' => $each_parent,
'parent_table' => NULL
];
}
traverseChildren($hierarchy,$each_parent,$result);
}
}
function traverseChildren($hierarchy,$parent,&$result){
foreach($hierarchy[$parent] as $each_child){
$result[] = [
'table' => $each_child,
'parent_table' => $parent
];
traverseChildren($hierarchy,$each_child,$result);
}
}
foreach(reorderHierarchy($data) as $each_data){
echo $each_data['table']," , ",(is_null($each_data['parent_table']) ? "NULL" : $each_data['parent_table']),"<br/>";
}
Output:
test , NULL
test , NULL
test2 , test
test3 , test
test5 , test3
test6 , test5
test4 , NULL
Demo: https://3v4l.org/AmJpY
Explanation:
Part 1:
function reorderHierarchy($data){
$hierarchy = [];
$top_level_parents = [];
foreach($data as $each_data){
$hierarchy[$each_data['table']] = array();
if(is_null($each_data['parent_table'])){
if(!isset($top_level_parents[$each_data['table']])){
$top_level_parents[$each_data['table']] = 0;
}
$top_level_parents[$each_data['table']]++;
}
}
foreach($data as $each_data){
if(!is_null($each_data['parent_table'])){
$hierarchy[$each_data['parent_table']][] = $each_data['table'];
}
}
$result = [];
traverseHierarchy($hierarchy,$top_level_parents,$result);
return $result;
}
In the above function, we are creating 2 kinds of arrays, namely, $hierarchy and $top_level_parents. $hierarchy is an array where every key has it's children keys inside it. $top_level_parents is an array which collects all tables who doesn't have any parent with key being table name and value being it's occurrences.
We then call another function traverseHierarchy to traverse over all of these top level parents and get their children as well. This way, we will always visit parents before children, since we are iterating parents first(even valid for it's children who would be in turn parents for other tables).
To better explain, both arrays would look something like below:
$hierarchy:
Array
(
[test] => Array
(
[0] => test2
[1] => test3
)
[test2] => Array
(
)
[test4] => Array
(
)
[test5] => Array
(
[0] => test6
)
[test6] => Array
(
)
[test3] => Array
(
[0] => test5
)
)
$top_level_parents:
Array
(
[test] => 2
[test4] => 1
)
Part 2:
function traverseHierarchy($hierarchy,$top_level_parents,&$result){
foreach($top_level_parents as $each_parent => $occurrences){
while($occurrences-- > 0){
$result[] = [
'table' => $each_parent,
'parent_table' => NULL
];
}
traverseChildren($hierarchy,$each_parent,$result);
}
}
Here, we are iterating over all top level parents, storing them in the result array with the no. of times they occurred in the original array.
Once done, we would now traverse it's children and include all it's children in the result array as well.
Part 3:
function traverseChildren($hierarchy,$parent,&$result){
foreach($hierarchy[$parent] as $each_child){
$result[] = [
'table' => $each_child,
'parent_table' => $parent
];
traverseChildren($hierarchy,$each_child,$result);
}
}
Here, we iterate over all children and include them in the result. It is quite possible that this child could have it's own children as well. So, we would recursively collect them all using depth first search. This way we are always ensuring parent comes before child.
In the last section, we just print the result.
test2 times?test, NULLfrom the other?