PHP: цикл через многомерный массив и установление отношений между родителями и дочерними элементами между элементами массива

Я разрабатываю систему управления контентом, и у меня возникла проблема с дочерними родительскими отношениями элементов в CMS.

В основном у меня есть система, которая может создавать страницы, и когда страница создается, вы можете выбрать родительскую страницу для поднавигации. Это все нормально и денди, пока я не попытаюсь создать навигацию из БД.

Я не уверен, что какое-то соединение будет лучше, но я предпочитаю получать все данные в массиве и манипулировать массивом с помощью php.

Мой массив результатов из БД выглядит так:

Array ( [0] => Array ( [id] => 27 [name] => home [link] => home.html [parent] => 0 ) [1] => Array ( [id] => 30 [name] => about [link] => about.html [parent] => 27 ) ) 

Мне нужно пройти через массив, подобный этому, который может иметь любое количество навигации и разумно сортировать его в своих родительских дочерних отношениях. Я смог это сделать, но только на одном уровне. Он должен управлять детьми с детьми с детьми и т. Д. С бесконечным количеством слоев и выводить его на неупорядоченные вложенные списки HTML.

 <ul> <li> <a>Nav</a> <ul> <li> <a>Subnav</a> <ul> <li> <a>Sub Sub nav</a> </li> </ul> </li> </ul> <li> <ul> 

И т.д. …

Я не думаю, что вы должны попасть в объекты. Плюс я думаю, что это просто дополнительная работа для создания объектов и т. Д. По-моему, вы должны прокручивать массив и генерировать многомерный массив, представляющий навигационную иерархию, а затем циклически сгенерировать результирующий массив для генерации вашего HTML. Я сделал образец кода для вас, он работает так, как вы хотите, но вы, вероятно, захотите внести некоторые изменения.

функции

 // Generate your multidimensional array from the linear array function GenerateNavArray($arr, $parent = 0) { $pages = Array(); foreach($arr as $page) { if($page['parent'] == $parent) { $page['sub'] = isset($page['sub']) ? $page['sub'] : GenerateNavArray($arr, $page['id']); $pages[] = $page; } } return $pages; } // loop the multidimensional array recursively to generate the HTML function GenerateNavHTML($nav) { $html = ''; foreach($nav as $page) { $html .= '<ul><li>'; $html .= '<a href="' . $page['link'] . '">' . $page['name'] . '</a>'; $html .= GenerateNavHTML($page['sub']); $html .= '</li></ul>'; } return $html; } 

** использование образца **

 $nav = Array ( Array ( 'id' => 27, 'name' => 'home', 'link' => 'home.html', 'parent' => 0 ), Array ( 'id' => 30, 'name' => 'about', 'link' => 'about.html', 'parent' => 27 ) ); $navarray = GenerateNavArray($nav); echo GenerateNavHTML($navarray); 

Вероятно, вы можете сделать обе вещи за один шаг, но я думаю, что сначала создать многомерный массив. Удачи!

Решение Saad Imran работает хорошо, за исключением того, что вы хотите, чтобы в одном списке было несколько элементов навигации. Мне пришлось изменить несколько строк, чтобы заставить его создать проверенный список элементов. Я также добавил отступы, чтобы сделать сгенерированный код более удобочитаемым. Я использую пробелы, но это можно легко изменить на вкладки, если вы предпочитаете, просто замените 4 пробела на "\t" . (обратите внимание, что вы должны использовать двойные кавычки, а не одинарные кавычки, иначе php не заменяет символ табуляции, а фактически a \ t)

Я использую это в codeigniter 2.1.0 с суперфишей (PHP5)

 function GenerateNavHTML($nav, $tabs = "") { $tab=" "; $html = "\n$tabs<ul class=\"sf-menu\">\n"; foreach($nav as $page) { //check if page is currently being viewed if($page['link'] == uri_string()) { $html .= "$tabs$tab<li class=\"current\">"; } else { $html .= "$tabs$tab<li>"; } $html .= "<a href=\"$page[link]\">$page[name]</a>"; //Don't generate empty lists if(isset($page['sub'][0])) { $html .= $this->GenerateNavHTML($page['sub'], $tabs.$tab); } $html .= "</li>\n"; } $html .= $tabs."</ul>\n"; return $html; } 

Я получал (переключился на ol для разъяснения)

 1. Home 1. About Us 1. Products 1. sub-product 1 1. sub-product 2 1. Contact 

теперь я получаю

 1. Home 2. About Us 3. Products 1. sub-product 1 2. sub-product 2 4. Contact 

Красиво сгенерированный HTML

 <ul class="sf-menu"> <li class="current"><a href="index.html">Home</a></li> <li><a href="about.html">About Us</a></li> <li><a href="products.html">Products</a> <ul class="sf-menu"> <li><a href="products-sub1.html">sub-product 1</a></li> <li><a href="products-sub2.html">sub-product 2</a></li> </ul> </li> <li><a href="contact.html">Contact</a></li> </ul> 

Самый простой способ сделать это, вероятно, будет использовать объекты. Их можно легко манипулировать, а также легко создавать из массива, который вы получаете из базы данных.

Например, каждый объект имеет:

  1. идентификатор
  2. имя
  3. ссылка
  4. ссылка объекта на родительский объект
  5. список дочерних объектов

Вам понадобится статическая функция, которая сможет узнать, какой объект имеет определенный идентификатор базы данных. Это делается во время создания нового объекта, помещая ссылку в статический список. Затем этот список можно вызвать и проверить во время выполнения.

Остальное довольно просто:

 $arr = get_array_from_database(); foreach($arr as $node){ $parent_object = get_parent_object($node_id); $parent_object.subnodes[] = new NodeObject($node); } 

Что касается возврата объектов в список, это лучше всего сделать рекурсивно;

 function return_in_list($objects) { foreach($objects as $node) { echo '<li>'; echo '<a>' + node.link + '</a>'; if(node.subnodes.length > 0) { echo '<ul>'; return_in_list($node.subnodes); echo '</ul>'; } puts '</li>' } } 

Я работаю над одним и тем же проектом, и я не нашел нужным использовать объекты. База данных в значительной степени заботится о структуре, а функции php могут сделать все остальное. Мое решение было иметь родительское поле для страниц, которое указывает на имя раздела, как и у вас, но также добавить родительское поле для таблицы разделов, чтобы разделы могли указывать на другие разделы в качестве родителей. Он по-прежнему очень управляем, потому что есть только два родительских поля, которые нужно отслеживать, по одному для каждой таблицы, и я могу вложить в свою структуру столько уровней, сколько необходимо в будущем.

Поэтому для динамического создания дерева я собираюсь рекурсивно проверять родителей, пока не нажму нуль, что указывает на то, что текущий элемент находится на корне doc. Таким образом, нам не нужно знать какие-либо детали текущей структуры страницы в коде функции, и мы можем сосредоточиться только на добавлении и упорядочении страниц в mysql. Поскольку на другом плакате появилось меню html, я подумал, что добавлю здесь пример для динамического маршрута, потому что темы настолько похожи.

ДАННЫЕ

 // Sample 3 x 2 page array (php / html pages) // D1 key = page name // D2 element 1 = page name for menu display // D2 element 2 = parent name (section) $pages = Array ( 'sample-page-1.php' => Array ( 'M' => 'page 1', 'P' => null ), 'sample-page-2.php' => Array ( 'M' => 'page 2', 'P' => 'hello' ), 'sample-page-3.php' => Array ( 'M' => 'page 3', 'P' => 'world' ) ); // Sample 2 x 1 section array (parent directories) // D1 key = section name // D2 element = parent name (if null, assume root) $sections = Array ( 'hello' => null, 'world' => 'hello' ); $sep = ' > '; // Path seperator $site = 'test.com'; // Home string 

ФУНКЦИИ

 // Echo paragraph to browser function html_pp ( $text ) { echo PHP_EOL . '<p>' . sprintf ( $text ) . '</p>' . PHP_EOL; } // Get breadcrumb for given page function breadcrumb ( $page ) { // Reference variables in parent scope global $pages; global $sections; global $sep; global $site; // Get page data from array $menu = $pages [ $page ] [ 'M' ]; $parent = $pages [ $page ] [ 'P' ]; if ( $parent == null ) { $path = $site . $sep . $menu; } else { $path = $site . $sep . get_path ( $parent ) . $sep . $menu; } return $path; } // Trace ancestry back to root function get_path ( $parent ) { // Reference variables in parent scope global $sections; global $sep; if ( $sections [ $parent ] == null ) { // No more parents return $parent; } else { // Get next parent through recursive call return get_path ( $sections [ $parent ] ) . $sep . $parent; } } 

ПРИМЕНЕНИЕ

 // Get breadcrumbs by page name $p1 = 'sample-page-1.php'; $p2 = 'sample-page-2.php'; $p3 = 'sample-page-3.php'; html_pp ( $p1 . ' || ' . breadcrumb ( $p1 ) ); html_pp ( $p2 . ' || ' . breadcrumb ( $p2 ) ); html_pp ( $p3 . ' || ' . breadcrumb ( $p3 ) ); // or use foreach to list all pages foreach ( $pages as $page => $data) { html_pp ( $page . ' || ' . breadcrumb ( $page ) ); } 

ВЫВОД

sample-page-1.php || test.com> стр. 1

sample-page-2.php || test.com> hello> стр. 2

sample-page-3.php || test.com> hello> мир> страница 3