Многоуровневое меню на PHP + MySQL

Многоуровневое меню на PHP + MySQL

Многоуровневое меню на PHP + MySQL

Ни один сайт не обходится без навигации или как еще называют "меню сайта". Так вот меню сайта бывает одноуровневым и многоуровневым в виде дерева. Если с одноуровневым меню особых сложностей в плане реализации не возникает, то при создании многоуровневого меню нужно хорошо подумать.

Самое главное в этой задаче это спроектировать базу данных для нашего многоуровневого меню. Создадим таблицу Categories с тремя полями id , title , parent где:

  • ID – идентификатор
  • Title – Название меню
  • Parent – Родитель категории по умолчанию 0

За ветвление меню отвечает поле Parent если Parent = 0 , то эта категория является родительской. Для того чтобы добавить потомков к родительской категории нужно в поле parent указать ID нужного родителя. Например:

Таблицы с категориями
ID TITLE PARENT
1 Автомобили 0
2 Мотоциклы 0
3 Мазда 1
4 Хонда 1
5 Кавасаки 2
6 Харлей 2
7 Лодки 0

Как видно из таблицы, у родительской категории Автомобили есть два потомка – это Мазда и Хонда связанных по полю Parent . А у категории Мотоциклы два потомка – это Кавасаки и Харлей . При этом у категории Лодки нет потомков. Надеюсь, что Вы поняли,как связать категории.

Далее переходим от слов к практике. Создадим таблицу Categories.

  
    CREATE TABLE IF NOT EXISTS `categories` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `title` varchar(255) NOT NULL,
    `parent` int(10) unsigned NOT NULL,
    PRIMARY KEY (`id`)
    ) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=20 ;

    --
    -- Дамп данных таблицы `categories`
    --
    INSERT INTO `categories` (`id`, `title`, `parent`) VALUES
    (1, 'Автомобили', 0),
    (2, 'Мотоциклы', 0),
    (3, 'Мазда', 1),
    (4, 'Хонда', 1),
    (5, 'Кавасаки', 2),
    (6, 'Харлей', 2),
    (7, 'Мазда 3', 3),
    (8, 'Мазда 6', 3),
    (9, 'Седан', 7),
    (10, 'Хечбэк', 7),
    (11, 'Лодки', 0),
    (12, 'Лифтбэк', 8),
    (13, 'Кроссовер', 8),
    (14, 'Белый', 13),
    (15, 'Красный', 13),
    (16, 'Черный', 13),
    (17, 'Зеленый', 13),
    (18, 'Мазда CX', 3),
    (19, 'Мазда MX', 3);
  

Алгоритм работы состоит из следующего:

  • Создаем соединение с базой данных
  • Получаем все данные из таблицы Categories
  • Обрабатываем полученные данные изменив ключ массива на номер ID
  • Из обработанного массива строим дерево зависимостей неограниченной вложенности используя метод рекурсии для построения
  • Выводим наше многоуровневое меню на экран

Создаем соединение с базой данных

  
    <?php

    //Устанавливаем кодировку и вывод всех ошибок
    header('Content-Type: text/html; charset=UTF-8');
    error_reporting(E_ALL);

    //Объектно-ориентированный стиль
    $mysqli = new mysqli('localhost', 'user', 'password', 'database');

    //Устанавливаем кодировку utf8
    $mysqli->query("SET NAMES 'utf8'");

    /*
    * Это "официальный" объектно-ориентированный способ сделать это
    * однако $connect_error не работал вплоть до версий PHP 5.2.9 и 5.3.0.
    */
    if ($mysqli->connect_error) {
        die('Ошибка подключения (' . $mysqli->connect_errno . ') '
        . $mysqli->connect_error);
    }

    /*
    * Если нужно быть уверенным в совместимости с версиями до 5.2.9,
    * лучше использовать такой код
    */
    if (mysqli_connect_error()) {
        die('Ошибка подключения (' . mysqli_connect_errno() . ') '
        . mysqli_connect_error());
    }
  

Пишем функцию получения данных из таблицы Categories

  
    //Получаем массив нашего меню из БД в виде массива
    function getCat($mysqli){
        $sql = 'SELECT * FROM `categories`';
        $res = $mysqli->query($sql);

        //Создаем масив где ключ массива является ID меню
        $cat = array();
        while($row = $res->fetch_assoc()){
            $cat[$row['id']] = $row;
        }
        return $cat;
    }
  

Получаем массив вот такого вида, где ключ массива это ID категории.

Многоуровневое меню на PHP + MySQL

Далее нам нужно из этого массива построить дерево зависимости дочерних элементов от родительских проходя рекурсивно по всему массиву.

Функция построения дерева из массива от Tommy Lacroix

    
      //Функция построения дерева из массива от Tommy Lacroix
      function getTree($dataset) {
          $tree = array();

          foreach ($dataset as $id => &$node) {
              //Если нет вложений
              if (!$node['parent']){
                  $tree[$id] = &$node;
              }else{
                  //Если есть потомки то перебераем массив
                  $dataset[$node['parent']]['childs'][$id] = &$node;
              }
          }
          return $tree;
      }
    
  

Получаем массив в виде дерева

Многоуровневое меню на PHP + MySQL

Скрипт целиком

  
 >
<?php

//Устанавливаем кодировку и вывод всех ошибок
header('Content-Type: text/html; charset=UTF-8');
error_reporting(E_ALL);

//Объектно-ориентированный стиль
$mysqli = new mysqli('localhost', 'user', 'pass', 'db');

//Устанавливаем кодировку utf8
$mysqli->query("SET NAMES 'utf8'");

/*
 * Это "официальный" объектно-ориентированный способ сделать это
 * однако $connect_error не работал вплоть до версий PHP 5.2.9 и 5.3.0.
 */
if ($mysqli->connect_error) {
    die('Ошибка подключения (' . $mysqli->connect_errno . ') '
            . $mysqli->connect_error);
}

/*
 * Если нужно быть уверенным в совместимости с версиями до 5.2.9,
 * лучше использовать такой код
 */
if (mysqli_connect_error()) {
    die('Ошибка подключения (' . mysqli_connect_errno() . ') '
            . mysqli_connect_error());
}

//Получаем массив нашего меню из БД в виде массива
function getCat($mysqli){
    $sql = 'SELECT * FROM `categories`';
    $res = $mysqli->query($sql);

    //Создаем масив где ключ массива является ID меню
    $cat = array();
    while($row = $res->fetch_assoc()){
        $cat[$row['id']] = $row;
    }
    return $cat;
}

//Функция построения дерева из массива от Tommy Lacroix
function getTree($dataset) {
    $tree = array();

    foreach ($dataset as $id => &$node) {    
        //Если нет вложений
        if (!$node['parent']){
            $tree[$id] = &$node;
        }else{ 
            //Если есть потомки то перебераем массив
            $dataset[$node['parent']]['childs'][$id] = &$node;
        }
    }
    return $tree;
}

//Получаем подготовленный массив с данными
$cat  = getCat($mysqli); 

//Создаем древовидное меню
$tree = getTree($cat);

//Шаблон для вывода меню в виде дерева
function tplMenu($category){
    $menu = '<li>
        <a href="#" title="'. $category['title'] .'">'. 
        $category['title'].'</a>';
        
        if(isset($category['childs'])){
            $menu .= '<ul>'. showCat($category['childs']) .'</ul>';
        }
    $menu .= '</li>';
    
    return $menu;
}

/**
* Рекурсивно считываем наш шаблон
**/
function showCat($data){
    $string = '';
    foreach($data as $item){
        $string .= tplMenu($item);
    }
    return $string;
}

//Получаем HTML разметку
$cat_menu = showCat($tree);

//Выводим на экран
echo '<ul>'. $cat_menu .'</ul>';

?>
  

Результат работы

Многоуровневое меню на PHP + MySQL для админки

Если Вы хотите использовать данное меню в админке своего сайта, то нужно переписать пару функций tplMenu() , showCat() .

  
    <?php

    function tplMenu($category,$str)
    {
        if($category['parent'] == 0){
            $menu = '<option value="'.$category['id'].'">'.$category['title'].'</option>';
        }else{
            $menu = '<option value="'.$category['id'].'">'.$str.' '.$category['title'].'</option>';
        }

        if(isset($category['childs'])){
            $i = 1;
            for($j = 0; $j < $i; $j++){
                $str .= '→';
            }
            $i++;

            $menu .= showCat($category['childs'], $str);
        }

        return $menu;
    }

    /**
    * Рекурсивно считываем наш шаблон
    **/
    function showCat($data, $str){
        $string = '';
        $str = $str;
        foreach($data as $item){
            $string .= tplMenu($item, $str);
        }
        return $string;
    }

    //Получаем HTML разметку
    $cat_menu = showCat($tree, '');

    //Выводим на экран
    echo '<select><option value="0">Выбери '. $cat_menu .'</select>';
    ?>
  

Результат работы

В следующей статье мы научимся сортировать данное меню , до новых встреч.

Скачать: Многоуровневое меню на PHP + MySQL


Поддержи проект:

No-name
Роман
Ой, прошу прощения) Ответ на свое предложение по поводу выпадающего меню для админки нашел у вас же в отзывах к статье)))))

03/12/2016 17:04:53

Администрация
Администрация

Вынесу данный функционал ниже статьи для тех кому понадобится.

05/12/2016 08:49:08

No-name
Роман
Отличная статья вышла у Вас. Здорово все разжевано. Есть предложение по следующей статье, относящейся к многоуровневому меню, но на этот раз для админки. Пример: мне нужно сделать в админке выпадающий список для выбора категории для своей статьи, но выводиться должно все с отступами, например: html & css bootsrap faundation php ci yii js jquery ну и т.д. Заранее спасибо)

03/12/2016 16:47:00

Администрация
Администрация

Спасибо за отзыв.

05/12/2016 08:48:09

No-name
ильдар
> нужно как-то в коде добавить доп. массив, в котором будут храниться все предыдущие url (в вашем случае title) до родителя > для того, чтобы можно было, к примеру, на пятом уровне вложенности дерева вывести сразу нужную цепочку url (в вашем случае title) в результате у меня получилось сделать задуманное только через доп. запросы

<?php
$parent_key = 'id_parent';
$key1       = 'id';
$key2       = 'url';
$db_name    = 'db_name';
$id_        = $row[$key1]; // init
$urls       = array();
while ($id_ != 0) {
  $sql    = 'select * from ' . $db_name . ' where ' . $key1 . ' = ' . $id_;
  $result = mysql_query($sql);
  if (mysql_num_rows($result) &gt; 0) {
    while ($row = mysql_fetch_array($result)) {
      $id_    = $row[$parent_key];
      $urls[] = $row[$key2];
    }
  }
}
$urls = array_reverse($urls);
?>

03/11/2016 15:43:58

Администрация
Администрация

Если все таки Вам нужны такие ссылки, то взгляниете на эту статью Хлебные крошки многомерного меню PHP + MySQL там как раз есть механизм  получения родителей, немного поправьте код и у вас все получится!

07/11/2016 09:28:38

No-name
ильдар
> В базе где таблица с меню добавляешь поле url и пишешь в него что вам нужно, далее поправить шаблон я не об этом нужно как-то в коде добавить доп. массив, в котором будут храниться все предыдущие url (в вашем случае title) до родителя для того, чтобы можно было, к примеру, на пятом уровне вложенности дерева вывести сразу нужную цепочку url (в вашем случае title)

03/11/2016 10:27:59

Администрация
Администрация

Ильдар, понял что вам нужно сделать, вопрос в том для чего вам такие ссылки? Каково практическое применение? Может лучше было бы использовать ЧПУ (Человека подобный УРЛ). 

07/11/2016 09:24:59

No-name
ильдар
> Ильдар, создайте рядом с полем title еще одно поле и записывайте туда названия латиницей, далее в шаблоне выводите их. Вопрос в том зачем Вам такая строка? в вашем примере ссылки пустые (#) в моем случае нужно выводить накопительные ссылки про *создайте рядом с полем title еще одно поле* не совсем понял что нужно добавить в код, чтобы реализовать дерево ссылок /auto/, /auto/mazda/, /auto/mazda/mazda3/ ..

03/11/2016 10:11:23

Администрация
Администрация

В базе где таблица с меню добавляешь поле url и пишешь в него что вам нужно, далее поправить шаблон:

//Шаблон для вывода меню в виде дерева
function tplMenu($category){
	$menu = '<li>
		<a href="'. $category['url'] .'/" title="'. $category['title'] .'">'. 
		$category['title'].'</a>';
		
		if(isset($category['childs'])){
			$menu .= '<ul>'. showCat($category['childs']) .'</ul>';
		}
	$menu .= '</li>';
	
	return $menu;
}

03/11/2016 10:24:10

No-name
ильдар
как сделать накопительные ссылки вида /auto/ /auto/mazda/ /auto/mazda/mazda3/ /auto/mazda/mazda3/sedan/

03/11/2016 03:55:13

Администрация
Администрация

Ильдар, создайте рядом с полем title еще одно поле и записывайте туда названия латиницей, далее в шаблоне выводите их. Вопрос в том зачем Вам такая строка?

03/11/2016 10:05:36

No-name
Джордж
Вопрос - В каком каталоге должен лежать файл menu.php? Ответ - В каком Вам удобно. Вопрос - Если вы цепляете файл меню к базе и таблицам bez_reg, и другим в каком каталоге должен лежать файл menu.php? Если вы помогаете чайникам. Так помогайте, а не издевайтесь. Правильно было бы что бы все ваши лекции сводились воедино а не были разрознеными кусками...

11/09/2016 20:46:48

Администрация
Администрация

Джордж, во-первый какой был вопрос такой был ответ!

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

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

В третьих если есть какие-то трудности и Вам нужна помощь Вы всегда можете связаться со мной по почте bezramok-tlt@mail.ru

Удачного кодинга.    

11/09/2016 21:16:36

No-name
Джордж
В каком каталоге должен лежать файл menu.php?

11/09/2016 17:37:24

Администрация
Администрация

В каком Вам удобно.

11/09/2016 19:43:28

No-name
Анджей
Добрый день, голову ломаю, как реализовать.  у меня многоуровневое меню вот такого типа,  чтобы меню на сайте расположилось горизонтально я должен задать через css  классы для главной группы меню и чтобы при выводе они учитывались 

 <li class="fotos"><a href="index.php">Фотосессии</a>
  <li class="fototur"><a href="#">Фото-туризм</a>
   <li class="obrfo"><a href="designfoto.php">Обработка фото</a> 
   <li class="design"><a href="desvizfly.php">Дизайн</a>
и так далее, у меня вопрос если эти классы не обозначить то меню будет вертикальное так как css не задаст им свойства?

<div class="nav">
    <ul class="menu">   
    	<li class="fotos"><a href="index.php">Фотосессии</a>    
    		<ul>      
    			<li><a href="http://address post">Love Story</a></li>      
    			<li><a href="http://address post">Семейная</a></li>        
    			<li><a href="http://address post">Индивидуальная</a></li>        
    			<li><a href="http://address post">У моря</a></li>      
    		</ul>    
    	</li>    
    	<li class="fototur"><a href="#">Фото-туризм</a>    
    		<ul>      
    			<li><a href="#"></a></li>      
    			<li><a href="#"></a></li>                     
    		</ul>    
    	</li>        
    	<li class="obrfo"><a href="designfoto.php">Обработка фото</a>     
    		<ul>      
    			<li><a href="retush.php">Ретушь</a></li>      
    			<li><a href="fotomoncol.php">Фотомонтаж Коллаж</a></li>         
    			<li><a href="renovold.php">Востановление старых фотографий</a></li>      
    		</ul>    
    	</li>     
    	<li class="design"><a href="desvizfly.php">Дизайн</a>    
    		<ul>      
    			<li><a class="centr" href="designfobook.php">Фотокниги</a></li>      
    			<li><a class="centr" href="calendvig.php">Календари Виньетки</a></li>        
    			<li><a class="centr" href="designcut.php">Визитки</a></li>        
    			<li><a class="centr" href="designinvite.php">Флаера Приглашения</a></li>      
    		</ul>    
    	</li>    
    	<li class="osta"><a href="price.php">Стоимость</a></li>
        <li class="osta"><a href="contact.php">Контакты</a></li> 
    </ul>
</div>

01/07/2016 19:58:59

Администрация
Администрация

Совершенно верно. Для простоты я Вам рекомендую почитать или посмотреть ролики по Фреймворку Bootstrap. http://getbootstrap.com/components/#nav-dropdowns здесь пример с меню.

06/07/2016 16:33:34

No-name
Анджей
Большое спасибо

21/06/2016 11:36:25

Администрация
Администрация

Анджей и Вам спасибо.

21/06/2016 12:16:19


Copyright © 2014 - 2024 All rights reserved.