Вывод простого дерева.

Кухан

Новичок
Вывод простого дерева.

В базе (MySQL) храниться простое дерево каталогов:
Код:
id   title     parent
1   computer   0
2   php        1
3   smarty     2
4   delphi     1
5   cars       0
6   life       0
7   c++        1
8   audi       5
Нужно вывести все дерево в html типа:
Код:
cars
   audi
computer
   c++
   delphi
   php
      smarty
life
Уже везде обыскался, про эти деревья есть все, но функции (или хотя бы алгоритма) вывода простого дерева нет нигде.
Единственное, что нашел - это http://vingrad.ru/PHP-FAQ-002203 , но там плохо, что происходит много запросов к базе. Хотелось бы чтобы был один запрос, и его результат помещался например в массив. И уже с массивом велась работа.
 

SiMM

Новичок
http://phpclub.ru/faq/Tree

> Хотелось бы чтобы был один запрос
Nested sets

> и его результат помещался например в массив. И уже с массивом велась работа.
Что мешает сделать это самостоятельно, сделав один запрос?
 

Кухан

Новичок
вот нашел http://phpclub.ru/faq/Tree/AlPrintAll

Странно вроде я смотрел этот фак, наверное не заметил, потому что там написано: "Списки смежности (Adjacency List)", ни за что бы не догадался, что эти деревья так называются.

Автор оригинала: SiMM
> Хотелось бы чтобы был один запрос
Nested sets
Не хочется использовать да и разбираться с этим мощным скриптом, ради вывода какого-то небольшого дерева. Мне кроме вывода другие функции и не нужны.

P.S> тему можно закрыть.
 

SelenIT

IT-лунатик :)
В FAQ-е есть ссылка на способ вывести и Adjacency List в один запрос (обсуждение и пример кода на форуме http://dklab.ru). Правда, при таком подходе есть ограничение по вложенности (31 уровень для MySQL 4.0-), но на практике и это ограничение нечасто достигается...
 

Popoff

popoff.donetsk.ua
В FAQ-е есть ссылка на способ вывести и Adjacency List в один запрос (обсуждение и пример кода на форуме http://dklab.ru). Правда, при таком подходе есть ограничение по вложенности (31 уровень для MySQL 4.0-), но на практике и это ограничение нечасто достигается...
Нет такого ограничения при выводе всего дерева.
 

zarus

Хитрожопый макак
Извиняюсь за подъем старой темы, но последнее
"В FAQ-е есть ссылка на способ вывести и Adjacency List в один запрос (обсуждение и пример кода на форуме http://dklab.ru)" не нашел...
 

zarus

Хитрожопый макак
Не любит DK-Lab FireFox, ох не любит, из 5 попыток все 5 закончились вылетом браузера :|
Дома посмотрю.

З.Ы. А функции из FAQ по работе с AL я переделал малеха, только вот не знаю, имеет смысл их выкладывать. Все, что я там оставил - получение Узла, Потомков (1+, все уровни вложенности), Родителей (1+, до верхнего уровня), Узлы своего уровня (потомки родителя). Но это все делается рекурсивным выводом и хотелось бы оптимизировать.
Поэтому вопрос, поскольку там ни слова о GNU - "мона?" (с)
 

Popoff

popoff.donetsk.ua
zarus
Можно :) А в чем именно особенность твоих изменений?
 

zarus

Хитрожопый макак
Автор оригинала: Popoff
zarus
Можно :) А в чем именно особенность твоих изменений?
Ну скажем так, я формирую массив по-другому. Все постить здесь не имеет смысла, так как там много. Если есть желание посмотреть и раскритиковать, то могу выслать вечером на почту

-~{}~ 28.12.05 09:45:

Автор оригинала: baev
Что-то у Вас не то с firefox'ом. Или с расширениями.
1.5 в поставке as is.

>> Sorry за OFFTOP: Опера рулит
Не рулила и не будет рулить, рулит IE, который стоит на 95% компьютеров.
 

serglt

Анус, ой, Ахтунг
Вот вам дерево для этой таблицы :)
PHP:
<?
    function get_data ()
    {
        mysql_connect ("localhost", 'root', '');
        mysql_select_db ('test');
        
        $query = "SELECT id, title, parent from Three order by parent";
        $res = mysql_query ($query);
        
        $data = array ();
        while ($row = mysql_fetch_assoc ($res))
        {                            
            $data [] = $row;
        }  
        return $data;
    }               

    function print_three ($data, $parent = 0)
    {
        static $counter = 0;
        
        $counter += 5;
        for ($i = 0; $i < count ($data); $i ++)
        {
            if ($data [$i] ['parent'] == $parent)
            {
                for ($j = 0; $j < $counter; $j ++)
                    echo "&nbsp";
                echo $data [$i] ['title'] . "<br>";
                print_three ($data, $data [$i] ['id']);
            }
        }
        $counter -= 5;
    }
    
    $d = get_data ();
    print_three ($d);    
?>
-~{}~ 28.12.05 12:55:

Любое количество вложений :)
Рекурсия, чем больше записей тем больше время работы
 

zarus

Хитрожопый макак
Ну и зачем мне это? У меня есть, и в более удобном варианте. Мне не нужен вывод всего дерева. Мне нужна оптимизация запросов.
 

serglt

Анус, ой, Ахтунг
zarus ты что болеешь? С верху написано что челу ужно вывести ВСЕ дерево. Читай сначала прежде чем трындеть.
 

zarus

Хитрожопый макак
2sergIt
Прежде, чем написать ответ, стоит:
а) посмотреть на дату первого поста
б) прочитать тему до конца
в) подумать, а надо ли отвечать
----
То, что ты запостил есть в FAQ в куда более расширенном и понятном виде "made by popoff".
----
[offtopic]
2SiMM
И не лень за каждым BB-php прописывать?
[/offtopic]
 

zarus

Хитрожопый макак
Надеюсь, тему не перенесут на свалку.
Вот, что у меня получилось.
PHP:
// В БД должно быть 2 таблицы:
//    Дерево: tree (id,parent - unsigned int)
//    Данные: data (id,name,.. и т.д)
//    Связь таблиц по полю id
// Верхний уровень (не имеющий родителей) определяется полем parent = 0

function AL_print_Tree(&$tree) {
// Вывод дерева
// $tree     : массив, содержащий дерево
//   - предствляет из себя массив со вложенными массивами
//  Элементы массива:
//   id      : идентификатор узла
//   parent  : родитель узла
//   name    : данные об узле
//   tree    : подмассив, содержащий все дочерние узлы, имеющий аналогичную
//     структуру - (id,parent,name,tree)
  if (empty($tree) || (!is_array($element['tree'])) {
    return;
  }
  echo "<ul>";
  foreach ($tree as $element) {
    if ((empty($element['parent'])) && (count($tree)>0)) {
      echo '<hr />';
    }
    echo "<li>".$element['name'];
    AL_print_Tree($element['tree']);
    echo "</li>";
  }
  echo "</ul>";
}

function AL_load_Node($node) {
// Возвращает в виде массива указанный узел $node
//   $node   : id узла в таблице БД tree
  $res = mysql_query('
    SELECT
      tree.id,     #идентификатор элемента
      tree.parent, #идентификатор родительского элемента
                   #   элементы верхнего уровня содержат здесь 0
      data.name    #название
    FROM
      tree,        #дерево
      data         #данные
    WHERE
      tree.id=\''.$node.'\'
      and data.id = tree.id
    ');
  if ((!$res) || (false == mysql_num_rows($res))) {
    return false;
  }
  $tree = array();
  while (false !== ($row = mysql_fetch_assoc($res))){
    $tree[] = array(
      'id'     => $row['id'],
      'parent' => $row['parent'],
      'name'   => $row['name'],
      'tree'   => false,
    );
  }
  return $tree;
}

function AL_load_Children($node,$all=false,$depth=1) {
// Рекурсивная функция получения дочерних узлов заданного узла $node
//   $node   : id узла в таблице БД tree
//   $all    : флаг возврата всех дочерних узлов
//    - true : функция вернет все дочерние узлы массива
//    - false: функция вернет дочерние узлы массива на N уровней ниже $node
//   $depth  : количество уровней дочерних узлов, которые должна вернуть функция
//    - 1    : возвращает непосредственные дочерние узлы
//    - имеет смысл только при $all = false
  if ((!$all) && ($depth < 1)) {
    return false;
  }
  $res = mysql_query('
    SELECT
      tree.id,     #идентификатор элемента
      tree.parent, #идентификатор родительского элемента
                   #   элементы верхнего уровня содержат здесь 0
      data.name    #название
    FROM
      tree,        #дерево
      data         #данные
    WHERE
      tree.parent=\''.$node.'\'
      and
      data.id = tree.id
    ');
  if ((!$res) || (false == mysql_num_rows($res))) {
    return false;
  }
  while (false !== ($row = mysql_fetch_assoc($res))){
    $tree[] = array(
      'id'     => $row['id'],
      'parent' => $row['parent'],
      'name'   => $row['name'],
      'tree'   => AL_load_Children($row['id'],$all,--$depth),
    );
  }
  if (!empty($tree)) {
    return $tree;
  } else {
    return false;
  }
}

function AL_load_Parent($node,$all=false,$depth=1,&$child=false) {
// Рекурсивная функция получения родительских узлов заданного узла $node
//   $node   : id узла в таблице БД tree
//   $all    : флаг возврата всех родительских узлов
//    - true : функция вернет все родительские узлы массива
//    - false: функция вернет родительские узлы массива на N уровней выше $node
//   $depth  : количество уровней родительских узлов, которые должна вернуть функция
//    - 1    : возвращает непосредственный родительский узел
//    - имеет смысл только при $all = false
//   $child  : "внутренняя" переменная, с помощью которой организуется построение
//      родительских узлов, начиная с самого верхнего родителя
//    - при начальном вызове функции в эту переменную желательно ничего не передавать
//    - рекомендуется передавать массив, сформированный с помощью функций
//      AL_load_Children, AL_load_Siblings, AL_load_Node, чтобы организовать переопределение
//      родительского узла
  if ((empty($node)) || ((!$all) && ($depth < 1))) {
    return $child;
  }
  $res = mysql_query('
    SELECT
      t2.id,
      t2.parent,
      data.name
    FROM
      tree as t1 LEFT JOIN
      tree as t2 ON
        (t2.id = t1.parent),
      data
    WHERE
      t1.id = \''.$node.'\'
      and
      data.id = t2.id
    ');
  if ((!$res) || (false == mysql_num_rows($res))) {
    return $child;
  }
  while (false !== ($row = mysql_fetch_assoc($res))){
    $current = array(
      'id'     => $row['id'],
      'parent' => $row['parent'],
      'name'   => $row['name'],
      'tree'   => !$child ? false : array($child),
    );
    if (!empty($row['parent'])) {
      $tree[]  = AL_load_Parent($row['id'],$all,--$depth,$current);
    }
  }
  if (!empty($tree)) {
    return $tree;
  } elseif (!empty($current)) {
    return array($current);
  } else {
    return false;
  }
}

function AL_load_Siblings($node,$self=false) {
// Функция получения братских узлов заданного узла $node
//   $node   : id узла в таблице БД tree
//   $self   : флаг возврата заданного узла вместе с братскими
//    - false: заданный узел не включается в массив
//    - true : заданный узел включается в массив
  $res = mysql_query('
    SELECT
      t2.id,
      t2.parent,
      data.name
    FROM
      tree as t1 LEFT JOIN
      tree as t2 ON
        (t2.parent = t1.parent'.($self?'':' and t2.id != t1.id').'),
      data
    WHERE
      t1.id = \''.$node.'\'
      and
      data.id = t2.id
    ');
  if ((!$res) || (false == mysql_num_rows($res))) {
    return false;
  }
  while (false !== ($row = mysql_fetch_assoc($res))){
    $tree[] = array(
      'id'     => $row['id'],
      'parent' => $row['parent'],
      'name'   => $row['name'],
      'tree'   => false,
    );
  }
  if (!empty($tree)) {
    return $tree;
  } else {
    return false;
  }
}
// Примеры использования функций
// 1. Получение и вывод всего дерева
AL_print_Tree($a_tree = AL_load_Children(0,true));
echo '<hr />';
// 2. Получение и вывод первых двух уровней дерева
AL_print_Tree($a_tree = AL_load_Children(0,false,2));
echo '<hr />';
// 3. Получение и вывод непосредственных дочерних узлов узла с id=2
AL_print_Tree($a_tree = AL_load_Children(2));
echo '<hr />';
// 4. Получение и вывод всех дочерних узлов узла с id=2
AL_print_Tree($a_tree = AL_load_Children(2,true));
echo '<hr />';
// 5. Получение и вывод узла с id=1
AL_print_Tree($a_tree = AL_load_Node(1));
echo '<hr />';
// 6. Получение и вывод братских узлов узла с id=3
AL_print_Tree($a_tree = AL_load_Siblings(3));
echo '<hr />';
// 7. Получение и вывод всех родителей узла с id=4
AL_print_Tree($a_tree = AL_load_Parent(4,true));
echo '<hr />';
// 8. Получение и вывод непосредственного родителя узла с id=4
AL_print_Tree($a_tree = AL_load_Parent(4));
-~{}~ 28.12.05 14:25:

Простите за то, что нет комментариев, иначе бы это еще больше места заняло.
В кратце:
парметр $node - это id нужного нам в таблице элемента
параметр $all - вывод всего дерева (true), или только до определенного уровня +/- $depth (false,1+)
 

Popoff

popoff.donetsk.ua
zarus
а ты можешь сейчас прокомментировать. в частности, меня интересует, что изменилось кроме имен переменных, имен функций и цикла foreach вместо for. прокомментируй все, что ты поменял и добавил.
 

zarus

Хитрожопый макак
>> Ну скажем так, я формирую массив по-другому. Все постить здесь не имеет смысла, так как там много. Если есть желание посмотреть и раскритиковать, то могу выслать вечером на почту
Комментировать даже нечего. Изобрел "велосипед", только с одним колесом. У Вас сначала запрашивается все дерево. затем оттуда формируется массив, у меня массив формируется кучей запросов к базе... :| Ыххх...
 
Сверху