Funkcja rekurencyjna do generowania wielowymiarowej tablicy z wyniku bazy danych
Chcę napisać funkcję, która pobiera tablicę stron / kategorii (z płaskiego wyniku bazy danych) i generuje tablicę zagnieżdżonych elementów strony / kategorii na podstawie identyfikatorów nadrzędnych. Chciałbym to zrobić rekurencyjnie, aby można było wykonać dowolny poziom zagnieżdżania.
Na przykład: pobieram wszystkie strony w jednym zapytaniu i tak wygląda tabela bazy danych
+-------+---------------+---------------------------+
| id | parent_id | title |
+-------+---------------+---------------------------+
| 1 | 0 | Parent Page |
| 2 | 1 | Sub Page |
| 3 | 2 | Sub Sub Page |
| 4 | 0 | Another Parent Page |
+-------+---------------+---------------------------+
I to jest tablica, z którą chciałbym skończyć, aby przetworzyć moim zdaniem pliki:
Array
(
[0] => Array
(
[id] => 1
[parent_id] => 0
[title] => Parent Page
[children] => Array
(
[0] => Array
(
[id] => 2
[parent_id] => 1
[title] => Sub Page
[children] => Array
(
[0] => Array
(
[id] => 3
[parent_id] => 1
[title] => Sub Sub Page
)
)
)
)
)
[1] => Array
(
[id] => 4
[parent_id] => 0
[title] => Another Parent Page
)
)
Przejrzałem i wypróbowałem prawie każde rozwiązanie, z którym się spotkałem (jest ich wiele tutaj na Stack Overflow, ale nie miałem szczęścia w uzyskaniu czegoś na tyle ogólnego, że będzie działać zarówno na stronach, jak i w kategoriach.
Oto najbliższe, ale to nie działa, ponieważ przydzielam dzieci rodzicom pierwszego poziomu.function page_walk($array, $parent_id = FALSE)
{
$organized_pages = array();
$children = array();
foreach($array as $index => $page)
{
if ( $page['parent_id'] == 0) // No, just spit it out and you're done
{
$organized_pages[$index] = $page;
}
else // If it does,
{
$organized_pages[$parent_id]['children'][$page['id']] = $this->page_walk($page, $parent_id);
}
}
return $organized_pages;
}
function page_list($array)
{
$fakepages = array();
$fakepages[0] = array('id' => 1, 'parent_id' => 0, 'title' => 'Parent Page');
$fakepages[1] = array('id' => 2, 'parent_id' => 1, 'title' => 'Sub Page');
$fakepages[2] = array('id' => 3, 'parent_id' => 2, 'title' => 'Sub Sub Page');
$fakepages[3] = array('id' => 4, 'parent_id' => 3, 'title' => 'Another Parent Page');
$pages = $this->page_walk($fakepages, 0);
print_r($pages);
}
3 answers
Bardzo proste, ogólne Budowanie drzewa:
function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
$tree = buildTree($rows);
Algorytm jest dość prosty:
- Weź tablicę wszystkich elementów i id bieżącego rodzica (początkowo
0
/ nothing /null
/ whatever). - Pętla przez wszystkie elementy.
- Jeśli
parent_id
elementu odpowiada bieżącemu ID rodzica, które otrzymałeś w 1., elementem jest dziecko rodzica. Umieść go na swojej liście obecnych dzieci (tutaj:$branch
). - wywołanie funkcji rekurencyjnie o id element, który właśnie zidentyfikowałeś w 3., tj. Znajdź wszystkie dzieci tego elementu i dodaj je jako
children
element. - zwróć listę znalezionych dzieci.
Innymi słowy, jedno wykonanie tej funkcji zwraca listę elementów będących potomkami danego ID rodzica. Wywołaj go za pomocą buildTree($myArray, 1)
, zwróci listę elementów, które mają ID rodzica 1. Początkowo ta funkcja jest wywoływana z ID rodzica wynosi 0, więc zwracane są elementy bez id rodzica, które są węzły korzeniowe. Funkcja wywołuje się rekurencyjnie, aby znaleźć Dzieci Dzieci.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-29 13:14:10
Wiem, że to pytanie jest stare, ale miałem do czynienia z bardzo podobnym problemem-z wyjątkiem bardzo dużej ilości danych. Po kilku zmaganiach udało mi się zbudować drzewo w jednym przejeździe resultset-używając referencji. Ten kod nie jest ładny, ale działa i działa dość szybko. Nie jest rekurencyjny - oznacza to, że jest tylko jedno przejście nad zestawem wyników, a następnie jedno array_filter
na końcu:
$dbh = new PDO(CONNECT_STRING, USERNAME, PASSWORD);
$dbs = $dbh->query("SELECT n_id, n_parent_id from test_table order by n_parent_id, n_id");
$elems = array();
while(($row = $dbs->fetch(PDO::FETCH_ASSOC)) !== FALSE) {
$row['children'] = array();
$vn = "row" . $row['n_id'];
${$vn} = $row;
if(!is_null($row['n_parent_id'])) {
$vp = "parent" . $row['n_parent_id'];
if(isset($data[$row['n_parent_id']])) {
${$vp} = $data[$row['n_parent_id']];
}
else {
${$vp} = array('n_id' => $row['n_parent_id'], 'n_parent_id' => null, 'children' => array());
$data[$row['n_parent_id']] = &${$vp};
}
${$vp}['children'][] = &${$vn};
$data[$row['n_parent_id']] = ${$vp};
}
$data[$row['n_id']] = &${$vn};
}
$dbs->closeCursor();
$result = array_filter($data, function($elem) { return is_null($elem['n_parent_id']); });
print_r($result);
Podczas wykonywania na tych danych:
mysql> select * from test_table;
+------+-------------+
| n_id | n_parent_id |
+------+-------------+
| 1 | NULL |
| 2 | NULL |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 2 |
| 7 | 5 |
| 8 | 5 |
+------+-------------+
Ostatni print_r
daje to wyjście:
Array
(
[1] => Array
(
[n_id] => 1
[n_parent_id] =>
[children] => Array
(
[3] => Array
(
[n_id] => 3
[n_parent_id] => 1
[children] => Array
(
)
)
[4] => Array
(
[n_id] => 4
[n_parent_id] => 1
[children] => Array
(
)
)
)
)
[2] => Array
(
[n_id] => 2
[n_parent_id] =>
[children] => Array
(
[5] => Array
(
[n_id] => 5
[n_parent_id] => 2
[children] => Array
(
[7] => Array
(
[n_id] => 7
[n_parent_id] => 5
[children] => Array
(
)
)
[8] => Array
(
[n_id] => 8
[n_parent_id] => 5
[children] => Array
(
)
)
)
)
[6] => Array
(
[n_id] => 6
[n_parent_id] => 2
[children] => Array
(
)
)
)
)
)
Dokładnie tego szukałem.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-27 12:32:40
Możliwe jest użycie php, aby uzyskać wynik mysql do tablicy, a następnie użyć go.
$categoryArr = Array();
while($categoryRow = mysql_fetch_array($category_query_result)){
$categoryArr[] = array('parentid'=>$categoryRow['parent_id'],
'id'=>$categoryRow['id']);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-21 09:21:11