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

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

  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>
8
задан samjudson 6 February 2012 в 13:08
поделиться

6 ответов

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

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

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 являются всего лишь примерами, и я оставлю это на ваше усмотрение, чтобы реализовать их, как вам нужно, но я думаю, что это передает сообщение.

11
ответ дан 5 December 2019 в 04:32
поделиться

Я бы посоветовал вам изучить обход дерева с предварительным упорядочиванием. Есть статья по этому вопросу:

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

По сути, вы рассматриваете каждую страницу как "узел". Каждый узел имеет ссылку на своего родителя. Когда вы изменяете расположение узлов (добавляете дочерний узел, перемещаете узлы и т.д.), вы пересчитываете значения 'left' и 'right' для каждого узла (статья выше объясняет это очень подробно, со ссылками на исходный код в php). В итоге вы получаете возможность очень быстро определить, является ли данный узел прямым или косвенным дочерним узлом любого другого узла, а также получить все дочерние узлы данного узла.

4
ответ дан 5 December 2019 в 04:32
поделиться

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

Я знаю, что это не совсем похоже на ваш код, но я думаю, что вы сможете понять общую концепцию, если поймете рекурсию. если вы не понимаете рекурсию, проверьте 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>';
}
0
ответ дан 5 December 2019 в 04:32
поделиться

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);
2
ответ дан 5 December 2019 в 04:32
поделиться

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

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

  • ОДИН ВОПРОС
  • НЕТ РЕКУРСИИ

Я сам всегда использую такой подход.

Вставил сюда код - полностью работоспособен:

http://pastebin.com/GAFvSew4

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

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

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

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

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

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

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

16
ответ дан 5 December 2019 в 04:32
поделиться

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

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 родителя в стеке!

Мое предложение: обдумайте приведенный выше фрагмент кода и поймите его.

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

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

20
ответ дан 5 December 2019 в 04:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: