Перейти к содержимому

Сервис обмена электронных валют


  • Закрытая тема Тема закрыта

Создание древовидного меню

#1 RussiaStudent

RussiaStudent
  • Пользователь
  • 19 сообщений
  • Репутация: 0
0

Отправлено 20 Август 2009 - 22:41

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

Заинтересовало, так как хочется сделать выпадающий список категорий с n-ным числом вложенности, которые можно добавлять динамически.
Это конечно не новость, во многих, да и почти во всех CMS есть возможность создания древовидного меню, ну или имеется модуль.
Я же загорелся данной идеей так как она содержит в себе парочку нюансов:

1) Нам нужно выстроить список n-ного вложения каждого родителя, тут не обойтись циклами for. Хотя можно попробовать while, но не кажется это не рентабельным
2) Не совсем понятно как хранить подобные выпадающие списки, сколько таблиц для этого понадобится и как же выстроить структуру таблиц?

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

 

 

  • 0

#2 Евгений

Евгений
  • Пользователь
  • 481 сообщений
  • Репутация: 0

Отправлено 21 Август 2009 - 06:35

RussiaStudent, http://www.getinfo.ru/article610.html посмотрите вот эту статью, я сам ее не читал еще, но кажется то что надо
  • 0

#3 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 21 Август 2009 - 09:51

Евгений, Читая подобное, думаешь "да разве подобное усложнение нужно в связках древовидного меню". Правые и левые ключи за которыми нужно следить + очень много воздействий на базу данных, так что просто можно её повесить. Так же не очень удобная выборка по ключу, делать ограничения.
Я тут на досуге подумал, всех лучше для создания древовидного меню подойдет одна таблица с примерным содержанием полей
id INT(11) auto_increment - идентификатор узла
id_parent INT(11) - родительский узел
title VARCHAR(255) - заголовок узла
.................
Вот что из себя представляют данные
1 | 0 | узел 1
 2 | 0 | узел 2
 3 | 0 | узел 3
 4 | 0 | узел 4
 5 | 1 | узел 1_1
 6 | 1 | узел 1_2
 7 | 2 | узел 2_1
 8 | 2 | узел 2_2
 9 | 4 | узел 4_1
 10 | 4 | узел 4_2
 11 | 4 | узел 4_3
 12 | 4 | узел 4_4
 13 | 10 | узел 4_2_1
 14 | 10 | узел 4_2_2
 15 | 11 | узел 4_3_1
 16 | 11 | узел 4_3_2
Причем порядок следования узлов не важен, так как мы будем спускаться от 0 узла к максимальному. Все записи по уровням будут обработаны в последовательности.
Родительский узел 0 означает, что выбранный узел не имеет родителей.

Осталось написать функцию с помощью которой можно было бы осуществить постройку дерева и последующую обработку. Я думаю лучше всего бы было хранить связи в многомерном массиве на примере такого
array
(
   [id] => array
			  (
				 [id] => id
				 [id_parent] => id_parent
				 [title] => title
				 [child] => array
								(
								   [id] => array
											  (
												 [id] => id
												 [id_parent] => id_parent
												 [title] => title
												 [child] => array
																(
																   ...
																)
											   ),

								   [id] => array
											  (
												 [id] => id
												 [id_parent] => id_parent
												 [title] => title
												 [child] => array
																(
																   ...
																)
											   )
								)  
			  ),
.......................
)

  • 0

#4 yury

yury
  • Пользователь
  • 629 сообщений
  • Репутация: 176

Отправлено 21 Август 2009 - 09:58

RussiaStudent,
я не большой спец по базам данных, но мне кажется, что наиболее простой и, соответственно, правильный ;) алгоритм, примерно, такой:
* делаем одну табличку с 2мя полями: [id узла] - [id родителя]
* если в id родителя - 0, то это корень дерева
* для построения потомков конкретного узла делаем выборку по заданному id родителя
* для построения всего дерева делаем цикл по всем id узлов

ну вот, пока писал, ZiTosS уже что-то похожее закодил ;)
  • 0

#5 RussiaStudent

RussiaStudent
    Topic Starter
  • Пользователь
  • 19 сообщений
  • Репутация: 0

Отправлено 21 Август 2009 - 10:30

Спасибо всем за ответы. Структура понятна, красиво расписано.

Вообщем-то я тоже подумывал над одной таблице, так как для создания отдельных таблиц под каждую вложенность потребовало бы не малых затрат да и таблиц бы стало слишком много.
Постараюсь сегодня написать функцию обхода древовидного меню приведенного выше. Спасибо за наводку, действительно не так всё и сложно.

Интересует ещё всё же как легче написать обход дерева? Тут же нужен спуск по древу в каждой ветке. Читал когда-то про рекурсию - вызов самого себя. Может попробовать с помощью неё спуститься по дереву, как кто щитает правильный ли это подход. Или лучше написать обход с помощью while циклов?
  • 0

#6 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 21 Август 2009 - 22:56

RussiaStudent, рекурсия будет самое то. Только не забываем что рекурсивная функция либо должна возвращать массив детей, либо как-то его заносить во внутрь основного массива(тут можно массив по ссылке передавать)
С помощью while возможно не получится. Хотя можно, если составить сначала древо родителей, а потом его обходить и заносить. Но это сколько же лишнего кода. А вот в отношении рекрсии не так всё сложно. Она сама обойдет всё дерево, стоит только правильно функцию сформировать.
  • 0

#7 RussiaStudent

RussiaStudent
    Topic Starter
  • Пользователь
  • 19 сообщений
  • Репутация: 0

Отправлено 22 Август 2009 - 10:52

ZiTosS, вот я думаю как же всё таки написать эту функцию рекурсии, воот что-то подумал и написал такое:
function getChildMenu($massiv, $id_parent)
{
$resource=mysql_query("SELECT * FROM category WHERE id_parent={$id_parent}");
if(mysql_num_rows($resource)){
while($array=mysql_fetch_assoc($resource)){
$massiv[$array['id']]=$array;
// здесь вызов рекурсии, но как его организовать
}
}
}


Я тут подумал, как же заносить данные в массив, если в функции мы работаем с копией, и меняя её мы не меняем оригинал, на выходе мы опять же получем пустоту. И как же мне вызвать рекурсию?
  • 0

#8 ZiTosS

ZiTosS
  • Пользователь
  • 5 148 сообщений
  • Репутация: 8

Отправлено 22 Август 2009 - 11:55

RussiaStudent,

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

В языках программирование есть такое понятие, "Передача по ссылке" - что означает передача оригинала для работы с ним. Чтобы организовать в PHP передачу по ссылке достаточно перед переменной куда хотим передать ссылку поставить амперсанд ( & ):
function nameFunction(&$ref, $copy)
{
// $ref - переданное по ссылке ( изменяя в функции, изменим и оригинал )
// $copy - копия ( изменяя в функции, оригинал не изменяется )
$ref = 1;
$copy = 1;
}

$param1 = $param2 = 0;

# вызываем функцию.
nameFunction($param1, $param2);

// Теперь $param1 = 1; $param2 = 0

И как же мне вызвать рекурсию?

Сначала тебе нужно создать подмассив детей, ключем к нему будет "child". А затем для него вызвать рекурсию. Типа этого:
.......................
$massiv[$array['id']]['child'] = array(); // создаём элемент массива для хранения детей
getChildMenu($massiv[$array['id']]['child'], $array['id_parent']); // вызываем рекурсию в качестве параметров (элемент(массив) хранения детей, родительский уровень(будем захватывать элементы, принадлежащие родительскому id))
.......................

  • 0

#9 RussiaStudent

RussiaStudent
    Topic Starter
  • Пользователь
  • 19 сообщений
  • Репутация: 0

Отправлено 24 Август 2009 - 11:34

ZiTosS, спасибо всё получилось. Вот написал де функции рекурсивных, одна собирает массив, а другая обрабатывает его и создаёт HTML-меню.

Функция создания древовидного массива:
// Параметры: наш формирующийся массив $menu_child (по ссылке), наш текущий родительский уровень $id_parent (по умолчанию 0)
function getChildMenu(&$menu_child, $id_parent = 0) {
$resource=mysql_query("SELECT * FROM category WHERE id_parent={$id_parent}"); // вытаскиваем все меню(категории), которые соответствуют родительскому ID
// если записей не нуль
if(mysql_num_rows($resource) > 0) {
// обрабатываем в цикле все записи
while($array=mysql_fetch_array($resource)) {
$menu_child[$array['id']]=$array; // заносим в массив данные элемента(id, id_parent, title)
$menu_child[$array['id']]['child']=array(); // создаём раздел, куда будем детей добавлять
getChildMenu($con, $menu_child[$array['id']]['child'], $array['id']); // (рекурсивный вызов) в качестве параметров массив с ключем 'child' и текущий ID
}
}
}


Функция создания HTML (ul/li) меню:
// Параметры: переменная, куда будем формировать контент $content (по ссылке), массив из которого формируем (должен быть в специально форме)
function printChildMenu(&$content, $menu) {
// начинаем обход массива $menu в цикле по записям
foreach($menu as $value) {
// если у текущего элемента нет детей
if(count($value['child'])==0)
$content.="<li><a href='./products_{$value['id']}.html'>{$value['title']}</a></li>"; // заносим элемент списка li
// если у текущего элемента есть дети
else {
$content.="<li><a href='./products_{$value['id']}.html'>{$value['title']}</a><ul>"; // заносим элемент списка li и открываем список детей
printChildMenu($content, $value['child']); // (рекурсивный вызов) обходим детей элемента для формирования списка
$content.="</ul></li>"; // заносим конец списка детей.
}
}
}

  • 0

robot

robot
  • Пользователь PRO
  • 2 652 сообщений
  • Репутация: 85


Оформление форума – IPBSkins.ru