ФорумПрограммированиеPHP для идиотов → Вывод меню, имеющего подпункты из БД

Вывод меню, имеющего подпункты из БД

  • shass

    Сообщения: 6 Репутация: N Группа: Кто попало

    Spritz 18 сентября 2009 г. 7:47

    Здравствуйте!
    В БД имеются поля:
    [table]
    [tr]
    [td]first_item[/td]
    [td]second_item[/td]
    [td]third_item[/td]
    [/tr]
    [tr]
    [td]Имя родительского меню[/td]
    [td]Имя Подменю (либо 0, если его нет)[/td]
    [td]Имя Подменю Подменю (либо 0, если его нет)[/td]
    [/tr]
    [/table]

    Этим кодом я вывожу меню из БД (вывод только Родительского меню и его подменю. Третий пункт пока не вывожу):

    $cur_item = '';
    $i = 0;
    $query = mysql_query("SELECT first_item, second_item from menu");
    while( $row = mysql_fetch_assoc($query) ) {
    if( $cur_item != $row['first_item'] ) {
    if ( $i == 1 ) {
    echo "</ul></li>";
    $i == 0;
    }
    echo "<li>".$row['first_item']."</li>";
    $cur_item = $row['first_item'];
    }
    if( $i == 1 ) echo "<ul>";
    else echo "<li>".$row['second_item']."</li>";


    Подскажите, как мне организовать вывод так, чтобы код строился в таком виде:

    <ul>
    <li>Родительское меню, содержащее подменю
    <ul>
    <li>Подменю</li>
    </ul>
    </li>
    <li>Второе родительское меню, не содержащее подменю</li>
    <li>Третье родительское меню, не содержащее подменю</li>

    </ul>
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 18 сентября 2009 г. 8:45, спустя 57 минут 17 секунд

    Ответ и много интересного и полезного можно найти по запросу Деревья в БД

    В БД не поля, а таблицы. А вот уже в таблицах - поля.
    Структуру таблиц, обычно, описывают "поля сверху-вниз", а не "слева-направо":[tt]
    first_item Имя родительского меню
    second_item Имя Подменю (либо 0, если его нет)
    third_item Имя Подменю Подменю (либо 0, если его нет)[/tt]

    Я бы хранил меню в таблице c полями:[tt]
    id - номер пункта
    parent_id - номер родителя (если это корневой элемент, то значение is null)
    name - имя пункта
    [/tt]

    Так-как Вы ограничили себя в конечной (и неглубокой) вложенности, то запрос для выборки данных, можно построить примерно так…
  • shass

    Сообщения: 6 Репутация: N Группа: Кто попало

    Spritz 18 сентября 2009 г. 10:15, спустя 1 час 30 минут 44 секунды

    Спасибо, замечания учту.
    Мне показалось, или у Вашего сообщения должно быть продолжение с кодом?
    …запрос для выборки данных, можно построить примерно так…
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 18 сентября 2009 г. 11:07, спустя 51 минуту 50 секунд

    Должно было быть. Нагрузили работой.
    Тут два варианта можно предложить:
    - запросом выбирать только детей указанного родителях

    select
     id,
     parent_id,
     name
    from
     menu
    where
     parent_id = :parent or (parent_id is null and :parent is null)

    Для выбора корневых элементов, :parent указываем null
    А проходить по деткам и выбирать их деток - рекурсивной функцией на PHP. В этой же функции можно формировать html-код для вывода.

    - учитывая ограниченную вложенность можно одним запросом выгрести все данные в первоначально предложенном Вами виде:

    select
    m1.name level_1,
    m2.name level_2,
    m3.name level_3
    from menu m1
     left join menu m2
       on m1.id = m2.parent_id
     left join menu m3
       on m2.id = m3.parent_id

    Затем пройтись по строкам этой выборки и построить древовидную таблицу в PHP.
    Теперь осталось пройти рекурсивно по этому дереву и сформировать код для вывода.

    В общем выбор сводится:
    - или кучка запросов и сразу формировать код для вывода;
    - или один запрос и потом пройтись по его результату.

    А вообще лучше поискать в Сети тему о хранении деревьев в БД.
  • shass

    Сообщения: 6 Репутация: N Группа: Кто попало

    Spritz 18 сентября 2009 г. 11:07, спустя 6 секунд

    Большое спасибо за разъяснение.
    Различные статьи по деревьям в БД уже начал читать, надеюсь поможет.
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 18 сентября 2009 г. 11:10, спустя 2 минуты 44 секунды

    В продолжение второго варианта …
    Каждый узел меню должен хранить инфу:
    - имя
    - номер (для красоты)
    - список детей

    Примерно будет так:

    array(
    'name' => '',
    'id'   => '',
    'items'=> array(/*каждый элемент массива - точно такой, как описан выше.Ключем массива будет выступать id*/)


    Пример записи:

    $r - текущая строка, которую мы считали.
    // $menu - корень. Он не имеет имени = только детей, которые живут в 'items'
    $menu['items'][$r['id_1']]['name'] = $r['name_1']; - записали имя
    $menu['items'][$r['id_1']]['id']  = $r['id_1'];   - записали id узла
    //Теперь дети
    $menu['items'][$r['id_1']]['items'][$r['id_2']]['name'] = $r['name_2']; - имя ребенка
    $menu['items'][$r['id_1']]['items'][$r['id_2']]['id'] = $r['id_2'];   - номер ребенка
    // Теперь дети детей - внуки
    $menu['items'][$r['id_1']]['items'][$r['id_2']]['items']['name'] = $r['name_3']; - имя внука
    $menu['items'][$r['id_1']]['items'][$r['id_2']]['items']['id']   = $r['id_3'];   - номер внука

    Фигня та ещё :) Но принцип рабочий … при небольшой глубине вложенности.
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 18 сентября 2009 г. 11:32, спустя 22 минуты 23 секунды

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

    Первый вариант более красив (но меня он коробит частыми запросами к БД).
    В нём у вас есть функция, которой Вы передаете один параметр $parent_id.
    Эта функция должна:
    - выбрать всех деток, родителем которых значится этот parent_id
    - если дети есть, то с каждым ребенком необходимо сделать следующее:
    – показать его (у Вас - вывод name)
    – вызвать эту же функцию. Параметром дать id текущего ребенка - пусть проверит, есть ли у этого ребенка свои детки.

    В вашем случае нужно предусмотреть открытие/закрытие списков.
    Список стоит открывать, если в нём будут элементы-дети.
    Значит примерно так получается:

    ПОКАЗАТЬ_ДЕТЕЙ($родитель){
    $дети = выбрать_из_бд_детей($родитель);
    ЕСЛИ (дети есть){
     вывести "начало списка";
     ДЛЯ_КАЖДОГО($дети как $ребенок){
       вывести $ребенок[имя];
       ПОКАЗАТЬ_ДЕТЕЙ($ребенок[номер])
     }
     вывести "конец списка"
    }
    }

    Страшно :) Рекурсивная функция всегда должна иметь условие, при котором она не вызывает саму себя, а заканчивает выполнение.
    У нас таким условием выступает "у узла нет детей"

    Спустя 36 сек.
    Успехов!
  • shass

    Сообщения: 6 Репутация: N Группа: Кто попало

    Spritz 18 сентября 2009 г. 12:25, спустя 52 минуты 59 секунд

    Еще раз спасибо! =)
    Думаю, что после такой теории с приактическими примерами, теперь меню не проблема для меня :)

Пожалуйста, авторизуйтесь, чтобы написать комментарий!