Ну, для создания моего меню мое меню, я использую дб подобная структура как это
2 Services 0 3 Photo Gallery 0 4 Home 0 5 Feedback 0 6 FAQs 0 7 News & Events 0 8 Testimonials 0 81 FACN 0 83 Organisation Structure 81 84 Constitution 81 85 Council 81 86 IFAWPCA 81 87 Services 81 88 Publications 81
Для присвоения другого подменю для существующего подменю, я просто назначаю идентификатор его родителя его значением родительского поля. породите 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
являются всего лишь примерами, и я оставлю это на ваше усмотрение, чтобы реализовать их, как вам нужно, но я думаю, что это передает сообщение.
Я бы посоветовал вам изучить обход дерева с предварительным упорядочиванием. Есть статья по этому вопросу:
Управление иерархическими данными в MySQL
По сути, вы рассматриваете каждую страницу как "узел". Каждый узел имеет ссылку на своего родителя. Когда вы изменяете расположение узлов (добавляете дочерний узел, перемещаете узлы и т.д.), вы пересчитываете значения 'left' и 'right' для каждого узла (статья выше объясняет это очень подробно, со ссылками на исходный код в php). В итоге вы получаете возможность очень быстро определить, является ли данный узел прямым или косвенным дочерним узлом любого другого узла, а также получить все дочерние узлы данного узла.
Я бы использовал рекурсивную функцию.
Я знаю, что это не совсем похоже на ваш код, но я думаю, что вы сможете понять общую концепцию, если поймете рекурсию. если вы не понимаете рекурсию, проверьте 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>';
}
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);
С такой структурой базы данных, как ваша, можно создать все меню HTML с помощью одного запроса и без рекурсии .
Да, я повторю:
Я сам всегда использую такой подход.
Вставил сюда код - полностью работоспособен:
Перейдите к строке 67, чтобы увидеть интересную часть ("get_menu_html").
Основной цикл начинается со строки 85.
Есть пять «настраиваемых» фрагментов HTML:
(Код мог бы быть чище, если бы я не беспокоился о табуляции .)
SQL для создания и заполнения образца базы данных доступен в конце сценария.
Вы можете попытаться поделиться с нами своими мыслями.
Вот "дружественная к разработчикам" версия решения этой проблемы "один запрос, без рекурсии".
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
Когда у пункта меню есть подпункты, мы сохраняем его ID родителя в стеке:
array_push( $parent_stack, $parent );
И затем, мы немедленно обновляем $parent, делая его ID текущего пункта меню:
$parent = $option['value']['id'];
После того, как мы перебрали все его подпункты, мы можем вернуться на предыдущий уровень:
$parent = array_pop( $parent_stack );
Вот почему мы сохранили ID родителя в стеке!
Мое предложение: обдумайте приведенный выше фрагмент кода и поймите его.
Вопросы приветствуются!
Одно из преимуществ этого подхода я вижу в том, что он устраняет риск попадания в бесконечный цикл, что может произойти при использовании рекурсии.