Ну, чтобы создать мое меню в моем меню, я использую подобную структуру 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 с одним запросом и без рекурсии .
Да, я повторю:
Это подход, который я всегда использую сам.
Вставка кода здесь – полностью функциональная:
Перейдите к строке 67, чтобы увидеть интересную часть («get_menu_html»).
Основной цикл начинается с строки 85.
Существует пять «настраиваемых» HTML-фрагментов:
(Код может быть более чистым, если я не беспокоился о табуляции .)
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);
Вам нужно использовать рекурсию, но мой подход отличается от других, я создал класс, который обрабатывал каждое меню отдельно, а затем запрашивал результаты и группировал каждый элемент в своем отдельном объекте в соответствии с их родителями, организовывал его по уровням, а затем объединять все объекты в один … проверить 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
.