Как создать неограниченный уровень меню через PHP и mysql

Ну, чтобы создать мое меню в моем меню, я использую подобную структуру db, подобную этой

   2 Услуги 0
   3 Фотогалерея 0
   4 Главная 0
   5 Обратная связь 0
   6 Часто задаваемые вопросы 0
   7 Новости и события 0
   8 Отзывы 0
  81 FACN 0
  83 Структура организации 81
  84 Конституция 81
  85 Совет 81
  86 IFAWPCA 81
  87 Услуги 81
  88 Публикации 81

Чтобы назначить другое подменю для существующего подменю, я просто назначаю его родительский id как его значение родительского поля. parent 0 означает верхнее меню

теперь нет проблем при создании подменю внутри другого подменю

теперь это способ, которым я выбираю подменю для верхнего меню

<ul class="topmenu"> <? $list = $obj -> childmenu($parentid); //this list contains the array of submenu under $parendid foreach($list as $menu) { extract($menu); echo '<li><a href="#">'.$name.'</a></li>'; } ?> </ul> 

Я хочу это сделать.

Я хочу проверить, есть ли в другом меню другое дочернее меню

и я хочу продолжать проверять до тех пор, пока он не будет искать все дочернее меню, которое доступно

и я хочу отобразить его дочернее меню внутри его конкретного элемента списка, как это

 <ul> <li><a href="#">Home</a> <ul class="submenu"> ........ <!-- Its sub menu --> </ul> </li> </ul> 

Для этого вам нужно использовать рекурсивные функции. Технически, есть несколько способов сделать это, но рекурсия – действительно лучший вариант здесь .

Вот основные сведения о том, как это будет работать:

 function drawMenu ($listOfItems) { echo "<ul>"; foreach ($listOfItems as $item) { echo "<li>" . $item->name; if ($item->hasChildren()) { drawMenu($item->getChildren()); // here is the recursion } echo "</li>"; } echo "</ul>"; } 

Свойства и методы $item – это просто примеры, и я оставлю это для вас, чтобы реализовать их, но вам нужно, но я думаю, что он получает сообщение.

Вот «версия, отличная от разработчика », решение « одного запроса , без рекурсии » для этой проблемы.

SQL :

 SELECT id, parent_id, title, link, position FROM menu_item ORDER BY parent_id, position; 

PHP :

 $html = ''; $parent = 0; $parent_stack = array(); // $items contains the results of the SQL query $children = array(); foreach ( $items as $item ) $children[$item['parent_id']][] = $item; while ( ( $option = each( $children[$parent] ) ) || ( $parent > 0 ) ) { if ( !empty( $option ) ) { // 1) The item contains children: // store current parent in the stack, and update current parent if ( !empty( $children[$option['value']['id']] ) ) { $html .= '<li>' . $option['value']['title'] . '</li>'; $html .= '<ul>'; array_push( $parent_stack, $parent ); $parent = $option['value']['id']; } // 2) The item does not contain children else $html .= '<li>' . $option['value']['title'] . '</li>'; } // 3) Current parent has no more children: // jump back to the previous menu level else { $html .= '</ul>'; $parent = array_pop( $parent_stack ); } } // At this point, the HTML is already built echo $html; 

Вам просто нужно понять использование переменной $ parent_stack.

Это стек «LIFO» (Last In, First Out) – изображение в статье в Википедии стоит тысячи слов: http://en.wikipedia.org/wiki/LIFO_%28computing%29

Когда у опции меню есть подпараметры, мы сохраняем его родительский идентификатор в стеке:

 array_push( $parent_stack, $parent ); 

И затем мы сразу обновляем $ parent, делая его текущим значением параметра меню:

 $parent = $option['value']['id']; 

После того, как мы зациклим все его подпараметры, мы можем вернуться к предыдущему уровню:

 $parent = array_pop( $parent_stack ); 

Вот почему мы сохранили родительский идентификатор в стеке!

Мое предложение: созерцать фрагмент кода выше и понимать его.

Вопросы приветствуются!

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

С такой структурой базы данных, как ваша, можно создать целое меню HTML с одним запросом и без рекурсии .

Да, я повторю:

  • ОДИН ВОПРОС
  • НЕТ РЕЗЕРВИРОВАНИЯ

Это подход, который я всегда использую сам.

Вставка кода здесь – полностью функциональная:

http://pastebin.com/GAFvSew4

Перейдите к строке 67, чтобы увидеть интересную часть («get_menu_html»).

Основной цикл начинается с строки 85.

Существует пять «настраиваемых» HTML-фрагментов:

  1. открытие обертки меню (строка 83)
  2. закрытие оболочки меню (строка 122)
  3. пункт меню с открытием детей (строка 100)
  4. пункт меню с закрытием детей (строка 92)
  5. пункт меню без детей (строка 113)

(Код может быть более чистым, если я не беспокоился о табуляции .)

SQL для создания и заполнения базы данных примеров доступен в конце скрипта.

Вы можете попытаться сообщить нам свои мысли.

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

Управление иерархическими данными в MySQL

Эффективно, вы берете каждую страницу как «узел». У каждого узла есть ссылка на его родителя. Когда вы меняете макет узлов (добавляете ребенка, перемещаете узлы и т. Д.), Вы пересчитываете значение «слева» и «справа» для каждого узла (статья выше объясняет это подробно, со ссылками на исходный код в php ). В результате вы можете быстро определить, является ли данный узел прямым или непрямым дочерним элементом любого другого узла, а также получить все дочерние узлы данного узла.

alt text http://i.imagehost.org/0934/product_hier.jpg http://pastie.org/969286

 drop table if exists product; create table product ( prod_id smallint unsigned not null auto_increment primary key, name varchar(255) not null, parent_id smallint unsigned null, key (parent_id) )engine = innodb; insert into product (name, parent_id) values ('Products',null), ('Systems & Bundles',1), ('Components',1), ('Processors',3), ('Motherboards',3), ('AMD',5), ('Intel',5), ('Intel LGA1366',7); delimiter ; drop procedure if exists product_hier; delimiter # create procedure product_hier ( in p_prod_id smallint unsigned ) begin declare v_done tinyint unsigned default 0; declare v_depth smallint unsigned default 0; create temporary table hier( parent_id smallint unsigned, prod_id smallint unsigned, depth smallint unsigned default 0 )engine = memory; insert into hier select parent_id, prod_id, v_depth from product where prod_id = p_prod_id; /* http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html */ create temporary table tmp engine=memory select * from hier; while not v_done do if exists( select 1 from product p inner join hier on p.parent_id = hier.prod_id and hier.depth = v_depth) then insert into hier select p.parent_id, p.prod_id, v_depth + 1 from product p inner join tmp on p.parent_id = tmp.prod_id and tmp.depth = v_depth; set v_depth = v_depth + 1; truncate table tmp; insert into tmp select * from hier where depth = v_depth; else set v_done = 1; end if; end while; select p.prod_id, p.name as prod_name, b.prod_id as parent_prod_id, b.name as parent_prod_name, hier.depth from hier inner join product p on hier.prod_id = p.prod_id inner join product b on hier.parent_id = b.prod_id order by hier.depth, hier.prod_id; drop temporary table if exists hier; drop temporary table if exists tmp; end # delimiter ; call product_hier(3); call product_hier(5); 

http://pastebin.com/ariBn3pE

Вам нужно использовать рекурсию, но мой подход отличается от других, я создал класс, который обрабатывал каждое меню отдельно, а затем запрашивал результаты и группировал каждый элемент в своем отдельном объекте в соответствии с их родителями, организовывал его по уровням, а затем объединять все объекты в один … проверить pastebin для полного кода

я бы использовал рекурсивную функцию.

Я знаю, что это не совсем так, как ваш код, но я думаю, вы можете получить общую концепцию, если понимаете рекурсию. если вы не понимаете рекурсию, проверьте http://en.wikipedia.org/wiki/Recursion_(computer_science)

 $list = new List(); function print_menu($list) { echo '<ul>'; foreach($list as $item) { echo '<li><a href="#">' . $item->name . '</a>'; if($item->has_child) { print_menu($item); } echo '</li>'; } echo '</ul>'; } 

Я нашел этот путь, работая с Yii Framework.

 $children = array(); foreach($model as $k => $item){ if(empty($item->cn_id_menu_padre)) $children[$item->cn_id] = $item->attributes; else $children[$item->cn_id_menu_padre]['hijos'][] = $item->attributes; } foreach($children as $k=>$child){ if(array_key_exists('hijos',$child)) { echo 'li y dentro ul<br>'; foreach($child['hijos'] as $hijo){ echo 'li<br>'; } } else echo 'li<br>'; } 

Если вам нужен еще один уровень, вы можете сделать еще один уровень в hijos_de_hijos массиве, hijos_de_hijos как hijos_de_hijos и выполнить сравнение в выражении if.

Конечно, для сравнения, если cn_id_menu_padre пуст, значение в базе данных должно быть null .