Рекурсивная функция для формирования многомерного массива из результата базы данных
Я ищу, чтобы написать функцию, которая принимает массив страниц / категорий (из плоского результата базы данных) и генерирует массив вложенных элементов страницы/категории на основе родительских идентификаторов. Я хотел бы сделать это рекурсивно, так что любой уровень вложенности может быть сделано.
например: я выбираю все страницы в одном запросе, и это то, что таблица базы данных выглядит как
+-------+---------------+---------------------------+
| id | parent_id | title |
+-------+---------------+---------------------------+
| 1 | 0 | Parent Page |
| 2 | 1 | Sub Page |
| 3 | 2 | Sub Sub Page |
| 4 | 0 | Another Parent Page |
+-------+---------------+---------------------------+
и это массив, который я хотел бы в конечном итоге обработать на мой взгляд файлы:
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
)
)
Я посмотрел и попробовал почти каждое решение, с которым я столкнулся (здесь их много на Stack Overflow, но не повезло получить что-то достаточно общее, что будет работать как для страниц, так и для категорий.
вот самое близкое, что я получил, но это не работает, потому что я назначаю детей родителю первого уровня.
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 ответов:
некоторые очень простые, общие дерево здания:
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);алгоритм довольно прост:
- массив всех элементов и идентификатор текущего родителя (изначально
0/не/null/что угодно).- цикл через все элементы.
- если
parent_idэлемента соответствует текущему родительскому идентификатору, который вы получили в 1. элемент является дочерним элементом родителя. Поместите его в свой список текущих детей (здесь:$branch).- вызовите функцию рекурсивно с идентификатором элемента, который вы только что идентифицировали в 3., т. е. найти все дочерние элементы этого элемента и добавить их как
childrenэлемент.- возвращает список найденных детей.
иными словами, эта функция возвращает список элементов, которые являются потомками данного родителя идентификатор. Назовите его с
buildTree($myArray, 1), он вернет список элементов, которые имеют Родительский идентификатор 1. Изначально эта функция вызывается с родительским идентификатором 0, поэтому возвращаются элементы без родительского идентификатора, которые являются корневыми узлами. Функция вызывает себя рекурсивно, чтобы найти детей.
Я знаю, что этот вопрос старый, но я столкнулся с очень похожей проблемой - за исключением очень большого количества данных. После некоторой борьбы мне удалось построить дерево за один проход набора результатов-используя ссылки. Этот код не очень хорош, но он работает и работает довольно быстро. Это нерекурсивно - то есть, есть только один проход над результирующим набором, а затем один
array_filterВ конце:$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);при выполнении по этим данным:
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 | +------+-------------+последние
print_rпроизводит этот вывод: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 ( ) ) ) ) )это именно то, что я искал.
можно использовать php, чтобы получить результат mysql в массив, а затем использовать его.
$categoryArr = Array(); while($categoryRow = mysql_fetch_array($category_query_result)){ $categoryArr[] = array('parentid'=>$categoryRow['parent_id'], 'id'=>$categoryRow['id']); }
Comments