Реализуем древовидное меню в пользовательской части блога

 

Привет всем! Добро пожаловать на Блог RS! Сегодня, как видно из заголовка поста, мы с Вами реализуем древовидное меню в пользовательской части блога. Нам предстоит полностью переписать два файла, и немного поработать с таблицей в базе данных. О теории реализации древовидного меню мы говорили с Вами в предыдущем посте "Теория реализации древовидного меню на php". Если кто не читал этот пост, то прежде чем приступить к практике реализации обязательно прочитайте его.

 

 

Предисловие

 

Вступления для тех кто на блоге в первые. Во-первых очень рад, что Вы заглянули =) Во-вторых как мне кажется Вы ищите какую то определенную информацию, боюсь Вас разочаровать =( Данный пост, как и 95% постов на моем блоге, посвящены конкретной цели создать блог с нуля. Материал в данном посте скорее всего покажется Вам бессмысленным, так как тут правятся файлы которые были созданы пару месяцев назад, и если Вы на блоге впервые, то естественно у Вас нет этих файлов, и Вы не сможете разобраться что к чему. Тем не менее, просматривая код, вы все таки можете почерпнуть что-то для себя.

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

Если я Вас заинтересовал то подпишитесь на RSS ленту блога через ридер, или же по почте

 

Цели на сегодня

 

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

  • Добавим в таблицу menu, в нашей базе данных, новую колонку - podmenu. Назначение колонки Вы можете понять из названию, или же почитав про эту колонку в предыдущем посте
  • Далее нам придется переделать шаблон menu.html. Изменение не значительные но все же есть
  • На по следок переделать файл menu.php. Он будет переписан полностью, подробности в соответствующем разделе поста =)

Так как мы переделываем модуль меню который написали ранее, бы ло бы полезно освежить память почитав пост "Пишем простенькое меню для нашей CMS".

 

Новая колонка в базе данных

 

Приступим. Для примера я создал три пункта. Вот как выглядит у меня сейчас таблица menu

таблица menu в базе данных

Как видите "Подпункт 1" в колонке podmenu содержит число 3. В предыдущем посте я писал о назначение этого числа. Если кто не помнит, или не понял, то это число позволяет понять скрипту, что "Подпункт 1" является дочерним пунктом, пункта "Пункт 1". То есть если взглянуть на уже с генерированное меню, то мы увидим следующее

иерархия пунктов

Приведите свою таблицу приблизительно к такому же виду. В идеале у Вас должна быть точно такая же таблица. Поэтому желательно удалить Вашу таблицу и создать новую, с помощью sql запроса который я Вам дам:

Для того, что бы удалить таблицу зайдите в структуру базы данных

Структура базы данных

Выберете таблицу menu

таблица menu

И внизу, во всплывающем списке выберите "С отмеченными: Удалить"

удалить таблицу из базы данных

 Далее заходим в структуры базы данных и жмем на кнопку "SQL"

SQL запрос в базе данных

И в появившимся поле вставляем следующий SQL запрос

CREATE TABLE IF NOT EXISTS `menu` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`href` varchar(255) NOT NULL,
`position` int(10) NOT NULL,
`podmenu` int(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=5 ;

INSERT INTO `menu` (`id`, `name`, `href`, `position`, `podmenu`) VALUES
(1, 'Главная', '/', 1, 0),
(4, 'Подпункт 1', '', 3, 3),
(3, 'Пункт 1', '#', 2, 0);

Жмем "ОК". Если Вы все правильно сделали, то в Вашей базе данных будет создана таблица menu с тремя нужными нам пунктами.

Почему так важно содержимое этой таблицы? Так как в коде будет скрипт создающий

  • из пустой колонки href - категорию
  • из колонки href которая содержит "#" - простое имя пункта
  • из колонки которая содержит прямой адрес - прямую ссылку

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

Поэтому я настоятельно рекомендую или привести свою таблицу к такому же виду, или удалить и создать новую!!!

 

Редактируем шаблон menu.html

 

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

Теперь давайте исправим шаблон. Изменения не большие. Так как в шаблоне нам необходимо будет добавлять ссылку ( тег <a href=""></a> ) целиком то поменяем код-слова [_href] и [_link] на одно - [_station]. Вот полный код шаблона. Дополнительные пояснения ниже

					<!--МЕНЮ-->
<div class="menu_title">Меню</div>
<div style="margin-left:16px;">
[_divmenu]
<div class="menu_link" style="margin-left:[_style]px;">[_station]</div>
[_podmenu]
[_divmenu]
</div>
<!--МЕНЮ-->

Код-слово - [_style] - Как видите это код-слово идет сразу же после margin-left. Не сложно догадаться, что [_style] будет содержать число, на которое будет сдвигаться наш пункт в право, образуя "дерево" =)

Код - слово - [_podmenu] - Будет содержать html код дочернего пункта. То есть вместо него будет подставлен код этого же шаблона.

 

Переписываем модуль menu.php

 

Я Вам выложу полный код файла, после чего поясню работу тех двух функций которые содержатся в этом файле

function menu()
{
$result_index = mysql_query("SELECT * FROM menu ORDER BY position");//Выводим из базы данных все пункты меню
$myrow_index = mysql_fetch_array($result_index);
if($myrow_index != "")//Проверяем есть ли в базе данных записи
{//Если есть...
$sm_read = file("templates/menu.html");//...подключаем шаблон
$sm_read = implode("",$sm_read);//функция file() возвращаем массив, поэтому склеиваем его
preg_match("/\[_divmenu\](.*?)\[_divmenu\]/s",$sm_read,$div_menu);//Вырезаем из шаблона ту
//часть, которая будет повторяться
$i=0;//переменная позволяющая создать многомерный массив
do//Цикл do while
{
$commMASS[$i] = array($myrow_index[id],//0 - ID пункта
$myrow_index[name],//1 - Имя пункта
$myrow_index[href],//2 - Ссылка (# - нет ссылки; Пустота - Категория; "http://site.ru/page2.html" - Прямая ссылка)
$myrow_index[podmenu]);//3 - ID родительного пункта
$i++;//при каждом цикле меняем элемент многомерного массива на единицу
}
while($myrow_index = mysql_fetch_array($result_index));

$menu = station(0,$commMASS,$div_menu[1],0);//функция генерации пунктов стартовые параметры:
//0 - Вывод главных пунктов,
//$commMASS - Многомерный массив со всеми пунктами,
//$div_menu[1] - часть шаблона,
//0 - вложенность - стартовая вложенность нулевая

$menu = preg_replace("/\[_divmenu\].*?\[_divmenu\]/s",$menu,$sm_read);//Вместо [_divmenu]...[_divmenu]
//вклеиваем наш с генерированный код из $menu
}
else $menu = "";//Если записей нет, то вывести это сообщение
return $menu;//Выводим с генерированный html код
}
//-------------------------------------------------------------------
function station($stat,$commMASS,$temp,$BC)
{
for($i=0;isset($commMASS[$i]);$i++)//глобальный цикл for
{
if($commMASS[$i][3] == $stat)//если podmenu пункта равно переменной stat
{
$edd_tamp = $temp;//Так как на придется править шаблон,
//то лучше его сохранить в отдельную переменную, иначе нам придется
//пользоваться функцией file() чаще чем 1 раз, а это нагрузка на сервер

//Замены идентификаторов на переменные из базы данных
if($commMASS[$i][2] == "")$href = "<a href=\"index.php?cat=".$commMASS[$i][0]."\">".$commMASS[$i][1]."</a>";//если нет ссылки то генерируем категорию
else//если поле href не пусто
{
if($commMASS[$i][2] != "#")$href = "<a href=\"".$commMASS[$i][2]."\">".$commMASS[$i][1]."</a>";//если поле href не содержит значок "#" значит генерируем прямую ссылку
else $href = $commMASS[$i][1];//если содержит значок "#" значит пункт является не ссылкой, поэтому выводим только имя
}
$style = $BC * 10;//рассчитываем отступ от края в пикселях
$newBC = $BC + 1;//прибавляем к вложенности единицу
$podmenu = station($commMASS[$i][0],$commMASS,$temp,$newBC);//перезапускаем функцию с новыми параметрами:
//$commMASS[$i][0] - id обрабатываемого пункта
//$commMASS - массив со всеми пунктами
//$temp - шаблон
//$newBC - новый уровень вложенности

$edd_tamp = str_replace("[_podmenu]",$podmenu,$edd_tamp);//код-слово заместо которого вставляется пункты которые являются подпунктами
$edd_tamp = str_replace("[_style]",$style,$edd_tamp);//отступ от левого края в пикселях
$edd_tamp = str_replace("[_station]",$href,$edd_tamp);//Ссылка
$menu .= $edd_tamp;// Склеиваем весь с генерированный код в одну переменную
}
}
if(!isset($menu))return "";//если переменная menu не была создана то результатом будет пустота
else return $menu;//если создана переменная menu то выводим ее
}

Как видите в файле содержится две функции. О работе каждой подробнее:

 

  • Функция menu()

 

Запускается генерация пунктов меню именно с этой функции. Смысл ее работы, это сбор данных из базы и превращение этих данный в многомерный массив. После, запуск второй функции и вывод результата этой функции на экран пользователя. До цикла do while изменений особых нет. Кроме появления переменной i. Она позволяет определить элемент массива ( [0],[1],[2],...,[n] ) который скрипт заполняет в данный момент.

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

 

  • Функция station()

 

У этой функции есть параметры:

  • $stat - Число которое позволяет скрипту определить какие именно пункты необходимо вывести из массива. Стартовый запуск позволяет вывести все пункты которые имеют в колонке podmenu значение ноль
  • $commMASS - Массив со всеми пунктами из базы данных
  • $temp - часть шаблона которая была выдернута посредством  функции preg_match. Часть шаблона находится между код-словами [_divmenu] и [_divmenu]
  • $BC - Уровень вложенности. От нее зависит количество пикселей для элемента стиля margin-left. Уровень вложенности бесконечен. При первом запуске вложенность равна нулю, при расчете отступа для margin-left мы получим 0px. Для пункта "Подпункт 1" (Уровень вложенности - 1) отступ будет равен 10px

Функция проверяет каждый пункт, и обрабатывает лишь те у который podmenu ( $commMASS[$i][3] ) равен переменной stat

Ссылка, то бишь переменная href может содержать в себе ссылки трех видов.

  • В случае если администратор в поле "адрес", при создание меню, оставит пустоту. Скрипт выведет этот пункт как категорию ( index.php?cat=N )
  • В случае если администратор пропишет в поле "адрес" прямую ссылку. В нашем случае пункт "Главная" содержит ссылку - "/" ( то есть ссылку на главную страницу ), как вариант можно прописать полный путь - "http://site.ru/". Так вот, если администратор прописал прямую ссылку, то пункт будет являться прямой ссылкой
  • В случае есть администратор пропишет в поле "адрес" значок "#" то пункт будет являться не тегом <a href="#"></a> а простым именем.

$style содержит число которое является отступом для элемента стиля css margin-left. Расчитывается как вложенность умноженная на 10.

Для создания "дерева" используем прием перезапуска функции station() внутри самой функции station() Тем самым у нас появляется возможность вывести пункты меню которые являются не родительными, а дочерними. Перезапуск функции осуществляется с новыми параметрами:

  • $stat - будет содержать id родительного пункта ( $commMASS[$i][0] ) Это позволит выводить из массива пункты у которых колонка podmenu равна не нулю а идентификатору родительного пункта ( $commMASS[$i][0] )
  • $commMASS - массив со всеми пунктами. Этот параметр без изменения, мы все время прогоняем через фильтр один и тот же многомерный массив
  • $temp - копия шаблона. Как и многомерный массив без изменений!
  • $newBC - новый уровень вложенности. Рассчитывается как актуальный уровень плюс один ( $BC + 1 )

Не думаю, что у Вас появятся какие либо вопросы по данным функциям =) В самом конце функции идут привычные нам замены код-слов, и вывод результата.

Результатом этих двух функций будет меню которое Вы можете видеть на моем блоге =) ну или на скриншоте =)

 результат функций

Как вы можете заметить, пункт "Главная" ведет на главную страницу. Пункт "Пункт 1" не является ссылкой. А подпункт "Подпункт 1" ведет на страницу категории. Все работает! =)

 

Заключение

 

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

Если у Вас есть какие либо вопросы, то пользуйтесь формой ниже.

Всего Вам наилучшего! У меня все!

Исходник
 

Возможно Вам будут интересны следующие заметки

Комментарии (4)

Ваше имя *
Сайт
Ваш E-mail *
Ваше сообщение *
 
Вы не подтвердили условия политики конфиденциальности.
Прохор, 11 Марта 2012 г. 20:37 пишет:
Гость
Хороший материал, Алексей!

Но есть предложение по оформлению - не мог бы ты сделать код с подсветкой, примерно как на этом сайте http://ruseller.com/lessons.php?rub=37&id=1392

Это бы значительно улучшило твой блог. А то строк много и приходится вводить сначала в редактор, сохранять, а потом разбираться в подсвеченном коде.
Rio-Shaman, 11 Марта 2012 г. 21:14 пишет:
Автор
Пока что воздержусь. Половину скриптов которые есть не работают с тегам <pre>...</pre>. В основном работают с <pre><code>...</code></pre>. У меня на блоге используется первый вариант, поэтому код будет либо не подсвечиваться, либо отображаться некорректно, я уже пробовал... =(
Ответ для пользователя: Прохор
Роман, 07 Августа 2012 г. 15:51 пишет:
Гость
Приветствую еще раз! Такой вопрос: как можно реализовать вывод под категорий на странице категории. Для примера: http://mehanika-kirov.ru/catalog/slesarnyi-uchastok

категория слесарный участок.. в ней под категории:
Подъемники
Маслосменное оборудование

а в категориях уже товары (посты так сказать)
Rio-Shaman, 07 Августа 2012 г. 19:03 пишет:
Автор
Система реализованная в движке не подходит для решение таких задач. Но можно написать дополнительный модуль который бы выводил, перед анонсами заметок, список категорий которые вложенные в открытую категорию.

У каждой категории есть поле "под категория", с помощью которого можно отыскать и вывести на экран вложенные пункты
Ответ для пользователя: Роман