Размышления над древовидным меню

ieroglif

Новичок
Размышления над древовидным меню

Привет сообществу! Решил вот присоедениться и для начала имею такой вопрос.
Предварительно заглянул в FAQ на соответствующую тему, и подивился тому, сколько оказывается есть всяких способов "выращивания" деревьев. Я же пока знаю только один самый простой, поэтому именно им и воспользовался, когда возникла необходимость сделать древовидное меню. Т.е. для каждой вершины указывается её родитель. Соответственно, само меню формируется с помощью простой рекурсивной функции:
PHP:
function get_tree_menu ($parent_id = 0) {
	global $out;
	$result = mysql_query("SELECT id, parent_id, name FROM categories WHERE parent_id = '$parent_id'");
	while ($row = mysql_fetch_assoc($result)) {
		$out .= "d.add($row[cat_id],$parent_id,'$row[cat_menu]','index.php?cat=$row[cat_id]');\n"; 
                    // Это функция яваскрипта, формирующего внешний вид меню
		get_tree_menu($row['cat_id']);
	}
	return $out;
}
В целом здесь всё понятно и работает как надо. Но меня терзают смутные сомнения... Это получается по одному запросу к базе на каждый пункт меню! А их уже сейчас несколько десятков и скоро может набраться под сотню и больше. Не будет ли такое меню создавать чересчур большую нагрузку на сервер? Может лучше стоит использовать какой-нибудь другой вариант? Или всё-таки ничего страшного?
 

iceman

говнокодер
ieroglif

не легче ли сразу с базы вывести все данные?

а патом делай с ними че хочешь...
 

Groove

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

ieroglif

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

И кстати, о Nested Sets - в общих чертах начинаю слегонца врубацца, но сразу же возник вопрос ответа на который пока не разглядел: а вот при добавлении в такое дерево нового элемента, значения всех узлов, следующих после него, получается, должны быть изменены? И как это делается?
 

ieroglif

Новичок
OZ спасибо за сцылу. Понравилось. Вон чего, оказывается можно вытворять с массивами, ежели умеючи!

Ну и всё-таки хотелось бы разобраться с Nested Sets, а то я может что неправильно понял с самого начала... Вот допустим, есть какойнить элемент с узлами, например, 25 и 26. Добавляем к нему дочерний. Тогда у дочернего будут узлы 26 и 27, а у родительского правый узел должен поменяться на 28, и значения всех следующих за ним узлов тоже должны увеличиться на два? Или как?
 

planarik

Новичок
Я делаю так

PHP:
$result = mysql_query("SELECT id, parent_id, name FROM categories'"); 
function get_tree_menu ($parent_id = 0, $result) { 
    global $out; 
    mysql_data_seek($result,0)
    while ($row = mysql_fetch_assoc($result)) { 
         if ($row["parent_id"]!==$parent_id) continue;
        $out .= "d.add($row[cat_id],$parent_id,'$row[cat_menu]','index.php?cat=$row[cat_id]');\n";  
                    // Это функция яваскрипта, формирующего внешний вид меню 
        get_tree_menu($row['cat_id'], $result); 
    } 
    return $out; 
}
На мой взгляд эффективнее.
 

God

Новичок
ieroglif
Достаточно доступно написанная статья про nested sets http://webscript.ru/stories.php3?story=04/09/01/8197045
 

Lazarius

Новичок
пользуюсь таким алгоритмом:
$arr - просто массив строк результата запроса.
$graph - определяет необходимость упорядочивания массива
PHP:
	static public function buildMultiList($arr, $graph = true) {
		$newArr = array();
		if (!$arr) { return array(); }
		foreach ($arr as $value) {
			$id=intval($value['id']);
			$name=$value['name'];
			$parent_id=intval($value['parent_id']);
			
			if (!$newArr[$id]) {
				$newArr[$id] = array('childrens' => array());
				$newArr[$id]['id']=$id;
			}
			$newArr[$id]['name'] = $name;
			
			if (!$newArr[$parent_id]) {
				$newArr[$parent_id] = array('childrens' => array());
				$newArr[$parent_id]['id']=$parent_id;
			}
			$newArr[$parent_id]['childrens'][] = &$newArr[$id];
			
			if ($id) $newArr[$id]['parent'] = &$newArr[$parent_id];
			
		}
		if ($graph) {
			$nesting=0;
			$a = &$newArr[0];
			$sost = 1;
			do {
				$print = 0;
				switch ($sost) {
					case 1 :
						if ($a['childrens']) {
							++$nesting;
							$abuf = &reset($a['childrens']);
							unset($a);
							$a=&$abuf;
							$print = 1;
							break;
						}
					case 2 :
						--$nesting;
						$a = &$a['parent'];
						if ($a === NULL) {
							$sost = 4;
							break;
						}
					case 3 :
						$an = &next($a['childrens']);
						if ($an) {
 							++$nesting;
 							unset($a);
							$a = &$an;
							$print = 1;
							$sost = 1;
						}
						else {
							$print = 0;
							$sost = 2;
						}
					break;
				}
				if ($print) {
					$a['nesting'] = $nesting;
					$extArr[] = $a;
				}
			} while ($sost != 4);
			return $extArr;
		}
		else { return $newArr; }
	}
 

Alexandre

PHPПенсионер
прежде чем выбрать вид дерева, надо определиться, что же в конечно счете нужно. Каждое "дерево выращивается" под свои задачи.
 

dron4ik

Новичок
Alexandre
+1

ieroglif я тоже начинал с рекурсивного дерева, но когда дерево подросло до 500+ веток, "что-то начало тормозить" :)
Но прозрачность постоения такого дерева мне очень нравилась и поэтому НестедСетс я не стал использовать, да и ни к чему это было, задачей было выводить структуру <strict>каталогов</strict> разделов на сервере в виде дерева, типа "карта сервера".

моё дерево:)
при добавлении новой ветки, у меня в базу пишется:
name - название раздела(ветки) :)
level - уровень вложенности
parent_id родительский ид
tree - полный путь от корню к ЭТОМУ разделу

сразу скажу 4 пункт в моей реализации основной, при выводе всего дерева используется один запрос
select * from departs order by tree

в принципе это упрощенная версия того, что есть у меня, но вполне работоспособная.

Достаточно раширяемая структура, мне кажется.

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

Lazarius

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

ieroglif

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

-~{}~ 13.07.07 12:39:

planarik Принцип вроде понятен, а попробовал - не работает. Туплю где-то чтоли... Буду разбираться.

-~{}~ 13.07.07 12:46:

Lazarius Не, тут я с ходу не врублюсь... А есть какиенить преимущества по сравнению с этим вариантом? http://phpclub.ru/talk/showthread.p...248&rand=63
 

Lazarius

Новичок
ieroglif
мне кажется в моем варианте вывод проще гораздо, потому что массив на выходе упорядочен. А первая часть моего алгоритма вроде очень похожа на тот вариант
 

ieroglif

Новичок
dron4ik А вот интересно стало: для чего может пригодиться наличие полного пути от корня? Ну, для того, чтобы легко и просто отобразить этот самый путь - понятно. А ещё для чего?
 

God

Новичок
ieroglif "если надо например только вывести дерево целиком", то с nested sets нужен только один запрос 'SELECT * FROM table ORDER BY left_key'
 

Sokil.Dmytro

Новичок
почитал приведенную статью
http://webscript.ru/stories.php3?story=04/09/01/8197045

вопрос по поводу проверки целосности
тестил на 4.1.16 - проверки 4 и 5 вываливаются с ошибками. После медитации над маном

из
SELECT id, MOD((right_key - left_key) / 2) AS ostatok FROM table WHERE ostatok = 0
получил
SELECT id, MOD((right_key - left_key) , 2) AS ostatok FROM table GROUP BY ostatok HAVING ostatok = 0

и соответственно
из
SELECT id, MOD((left_key – level + 2) / 2) AS ostatok FROM table WHERE ostatok = 1
получил
SELECT id, MOD((left_key – level + 2) , 2) AS ostatok FROM table GROUP BY ostatok HAVING ostatok = 1

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