Получение измененной модели обхода дерева предзаказов (вложенный набор) в <ul>

Я пытаюсь получить мои данные, которые иерархически настроены с помощью модели обхода дерева в <ul>, чтобы показать на моем сайте.

Вот мой код:

function getCats($) { // retrieve all children of $parent $query = "SELECT max(rght) as max from t_categories"; $row = C_DB::fetchSingleRow($query); $max = $row["max"]; $result ="<ul>"; $query = "SELECT * from t_categories where lft >=0 and rght <= $max"; if($rs = C_DB::fetchRecordset($query)){ $p_right =""; $p_left =""; $p_diff=""; while($row = C_DB::fetchRow($rs)){ $diff = $row["rght"] -$row["lft"]; if($diff == $p_diff){ $result.= "<li>".$row['title']."</li>"; }elseif (($row["rght"] - $row["lft"] > 1) && ($row["rght"] > $p_right)){ $result. "<ul>"; $result.= "<li>".$row['title']."</li>"; }else{ $result.= "<li>".$row['title']."</li>"; } $p_right = $row["rght"]; $p_left = $row["lft"]; $p_diff = $diff; } } $result.= "</ul>"; return $result; } 

Вот моя примерная таблица:

 |ID | TITLE | lft| rght | |1 | Cat 1 | 1 | 16 | |18 | Cat 2 | 3 | 4 | |22 | Cat 3 | 5 | 6 | |28 | Cat 4 | 7 | 8 | |34 | Cat 5 | 9 | 9 | |46 | Cat 6 | 11 | 10 | |47 | Cat 7 | 13 | 12 | |49 | Cat 8 | 15 | 14 | 

Теперь он выводит что-то вроде:

  <ul> <li>Cat 1</li> <li>Cat 2</li> <li>Cat 3</li> <li>Cat 4</li> <li>Cat 5</li> <li>Cat 6</li> <li>Cat 7</li> <li>Cat 8</li> </ul> 

Может ли кто-нибудь сказать мне, почему или как он выводит список в иерархической структуре?

Связанная тема

Хорошо, давайте сделаем охоту за головами;)

Шаг 0 – Пример Sanitize:
Как уже упоминалось, данные вашего примера нарушены, так как он не определяет допустимый вложенный набор. Если вы взяли эти данные из приложения, вы должны проверить логику вставки / удаления.

Поэтому для тестирования я использовал санированную версию:
(MySQL здесь, так как он был первым под рукой)

 CREATE TABLE t_categories`( `id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, `title` VARCHAR(45) NOT NULL, `lft` INTEGER UNSIGNED NOT NULL, `rght` INTEGER UNSIGNED NOT NULL, PRIMARY KEY (`id`) ); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 1',1,16); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 2',2,3); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 3',4,7); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 4',5,6); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 5',8,13); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 6',9,12); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 7',10,11); INSERT INTO t_categories (title, lft, rght) VALUES ('Cat 8',14,15); 

Шаг 1 – Позвольте базе данных сделать заказ
Вложенные наборы, которые в первую очередь придуманы как удобный способ хранения деревьев в базах данных, поскольку они довольно легко запрашивают поддеревья, родительские отношения и, особенно интересны в этом случае, для порядка и глубины:

 SELECT node.title, (COUNT(parent.title) - 1) AS depth FROM t_categories AS node CROSS JOIN t_categories AS parent WHERE node.lft BETWEEN parent.lft AND parent.rght GROUP BY node.title ORDER BY node.lft 

Это вернет ваш набор, упорядоченный по порядку, начиная с корневого узла и до конца заканчивается в предварительном порядке. Самое главное, это добавит глубину каждого узла как положительное целое число, указав, сколько уровней узел ниже корня (уровень 0). Для приведенных выше данных примера результатом будет:

 title, depth 'Cat 1', 0 'Cat 2', 1 'Cat 3', 1 'Cat 4', 2 'Cat 5', 1 'Cat 6', 2 'Cat 7', 3 'Cat 8', 1 

В коде:

 // Grab ordered data $query = ''; $query .= 'SELECT node.title, (COUNT(parent.title) - 1) AS depth'; $query .= ' FROM t_categories AS node'; $query .= ' CROSS JOIN t_categories AS parent'; $query .= ' WHERE node.lft BETWEEN parent.lft AND parent.rght'; $query .= ' GROUP BY node.title'; $query .= ' ORDER BY node.lft'; $result = mysql_query($query); // Build array $tree = array(); while ($row = mysql_fetch_assoc($result)) { $tree[] = $row; } 

Получившийся массив будет выглядеть так:

 Array ( [0] => Array ( [title] => Cat 1 [depth] => 0 ) [1] => Array ( [title] => Cat 2 [depth] => 1 ) ... ) 

Шаг 2 – Вывод в виде фрагмента списка HTML:

Использование цикла while:

 // bootstrap loop $result = ''; $currDepth = -1; // -1 to get the outer <ul> while (!empty($tree)) { $currNode = array_shift($tree); // Level down? if ($currNode['depth'] > $currDepth) { // Yes, open <ul> $result .= '<ul>'; } // Level up? if ($currNode['depth'] < $currDepth) { // Yes, close n open <ul> $result .= str_repeat('</ul>', $currDepth - $currNode['depth']); } // Always add node $result .= '<li>' . $currNode['title'] . '</li>'; // Adjust current depth $currDepth = $currNode['depth']; // Are we finished? if (empty($tree)) { // Yes, close n open <ul> $result .= str_repeat('</ul>', $currDepth + 1); } } print $result; 

Та же логика, что и рекурсивная функция:

 function renderTree($tree, $currDepth = -1) { $currNode = array_shift($tree); $result = ''; // Going down? if ($currNode['depth'] > $currDepth) { // Yes, prepend <ul> $result .= '<ul>'; } // Going up? if ($currNode['depth'] < $currDepth) { // Yes, close n open <ul> $result .= str_repeat('</ul>', $currDepth - $currNode['depth']); } // Always add the node $result .= '<li>' . $currNode['title'] . '</li>'; // Anything left? if (!empty($tree)) { // Yes, recurse $result .= renderTree($tree, $currNode['depth']); } else { // No, close remaining <ul> $result .= str_repeat('</ul>', $currNode['depth'] + 1); } return $result; } print renderTree($tree); 

Оба выводят следующую структуру:

 <ul> <li>Cat 1</li> <li> <ul> <li>Cat 2</li> <li>Cat 3</li> <li> <ul> <li>Cat 4</li> </ul> </li> <li>Cat 5</li> <li> <ul> <li>Cat 6</li> <li> <ul> <li>Cat 7</li> </ul> </li> </ul> </li> <li>Cat 8</li> </ul> </li> </ul> 

Nitpickers corner: Questioner явно запросил <ul> , но заказал неупорядоченные списки !? Давай…
😉

Лучшая функция рендеринга дерева, которая работала для меня ( php-функция для подготовки источника HTML для использования в jsTree jQuery-плагине ) вместо Henrik Opel:

 function MyRenderTree ( $tree = array(array('name'=>'','depth'=>'')) ){ $current_depth = 0; $counter = 0; $result = '<ul>'; foreach($tree as $node){ $node_depth = $node['depth']; $node_name = $node['name']; $node_id = $node['category_id']; if($node_depth == $current_depth){ if($counter > 0) $result .= '</li>'; } elseif($node_depth > $current_depth){ $result .= '<ul>'; $current_depth = $current_depth + ($node_depth - $current_depth); } elseif($node_depth < $current_depth){ $result .= str_repeat('</li></ul>',$current_depth - $node_depth).'</li>'; $current_depth = $current_depth - ($current_depth - $node_depth); } $result .= '<li id="c'.$node_id.'"'; $result .= $node_depth < 2 ?' class="open"':''; $result .= '><a href="#"><ins>&nbsp;</ins>'.$node_name.'</a>'; ++$counter; } $result .= str_repeat('</li></ul>',$node_depth).'</li>'; $result .= '</ul>'; return $result;} 

Результат HTML:

 <ul> <li id="c1" class="open"><a href="#"><ins>&nbsp;</ins>ELECTRONICS</a> <ul> <li id="c2" class="open"><a href="#"><ins>&nbsp;</ins>TELEVISIONS</a> <ul> <li id="c3"><a href="#"><ins>&nbsp;</ins>TUBE</a></li> <li id="c4"><a href="#"><ins>&nbsp;</ins>LCD</a></li> <li id="c5"><a href="#"><ins>&nbsp;</ins>PLASMA</a> <ul> <li id="c14"><a href="#"><ins>&nbsp;</ins>PLASMA1</a></li> <li id="c15"><a href="#"><ins>&nbsp;</ins>PLASMA2</a></li> </ul> </li> </ul> </li> <li id="c6" class="open"><a href="#"><ins>&nbsp;</ins>PORTABLE ELECTRONICS</a> <ul> <li id="c7"><a href="#"><ins>&nbsp;</ins>MP3 PLAYERS</a> <ul> <li id="c8"><a href="#"><ins>&nbsp;</ins>FLASH</a></li> </ul> </li> <li id="c9"><a href="#"><ins>&nbsp;</ins>CD PLAYERS</a></li> <li id="c10"><a href="#"><ins>&nbsp;</ins>2 WAY RADIOS</a></li> </ul> </li> </ul> </li> </ul> 

Существует пакет PEAR для работы с вложенными наборами: DB_NestedSet .
Вы также можете быть заинтересованы в статье Управление иерархическими данными в MySQL .

Это должно быть то, что вы ищете:

 function getCats($left = null, $right = null) { $sql = array(); $result = null; if (isset($left) === true) { $sql[] = 'lft >= ' . intval($left); } if (isset($right) === true) { $sql[] = 'rght <= ' . intval($right); } if (empty($sql) === true) { $sql[] = 'lft = 1'; } $sql = 'SELECT * FROM t_categories WHERE ' . implode(' AND ', $sql) . ';'; if ($rs = C_DB::fetchRecordset($sql)) { // you need to make sure that the query returns // something to correctly display the ULs if (empty($rs) === false) { $result .= '<ul>' . "\n"; while ($row = C_DB::fetchRow($rs)) { $result .= '<li>' . $row['title'] . '</li>' . "\n"; $result .= getCats($row['lft'], $row['rght']); } $result .= '</ul>' . "\n"; } } return $result; } 

Чтобы получить HTML для вашего вложенного дерева, вы должны сделать:

 echo getCats(); 

Обратите внимание, что ваш образец вложенного набора не выглядит правильным, также вы должны убедиться, что я не сделал никакой ошибки при вызове вашего класса C_DB, я не знаю, так как я не знаком с ним.

Просто зациклируйте результат:

 $sql = "SELECT node.name, (COUNT(parent.name) - 1) AS depth FROM nested_category AS node, nested_category AS parent WHERE node.lft BETWEEN parent.lft AND parent.rgt GROUP BY node.name ORDER BY node.lft"; $query_result = mysql_query($sql) $result = "<ul>"; $currDepth = 0; while($row = mysql_fetch_assoc($query_result)) { if($row['depth'] > $currDepth) { $result .= "<li><ul>"; // open sub tree if level up } if($row['depth'] < $currDepth) { $result .= str_repeat("</ul></li>", $currDepth - $row['depth']); // close sub tree if level down } $result .= "<li>$row['name']</li>"; // Always add node $currDepth = $row['depth']; } $result .= "</ul>"; echo $result; 
 $linaje=''; $lastnode=''; $sides['izq']=array(); $sides['der']=array(); $print = '<ul>'; foreach ($array as $key1 => $value1){ //Proyectos if(strpos($info[$key1]['linaje'],'-') !== false) $compare = strstr($info[$key1]['linaje'],'-',true); else $compare = $info[$key1]['linaje']; if($linaje != ''){ if ($linaje != $compare){ $linaje= $compare; $sides['izq']=array(); $sides['der']=array(); //for($i=1;$i <= substr_count($lastnode,'`')-substr_count($value1,'`');$i++) //$print .= '</ul></li>'; } } if ($lastnode != '') for($i=1;$i<= substr_count($lastnode,'`')-substr_count($value1,'`');$i++) $print .= '</ul></li>'; if (count($sides['der'])>0) if ($sides['der'][count($sides['der'])-1] > $info[$key1]['der']) $print .= '<ul>'; $print .= '<li><a href="#'.$info[$key1]['id'].'#'.$info[$key1]['linaje'].'">'.substr($value1,substr_count($value1,'`')).'</a>'; if ($info[$key1]['der'] - $info[$key1]['izq'] == 1) $print .= '</li>'; if ($key1 == count($info)-1) for($i=1;$i <= substr_count($lastnode,'`')-1;$i++) $print .= '</ul></li>'; $sides['der'][] = $info[$key1]['der']; $sides['izq'][] = $info[$key1]['izq']; if ($linaje =='') $linaje = $info[$key1]['linaje']; $lastnode = $value1; } $print .= '</ul>'; echo $print; 

разница в том, что вы можете отображать X чисел деревьев, это относится к одному из моих проектов. и я использую char как ссылку на глубину, когда я беру строки из БД

i`m используя запрос CROSS JOIN, отображающий jQuery меню jsTree; Все работает просто отлично! В существующей таблице я добавил столбец для позиции. Однако, когда я определяю позицию и упорядочиваю все по положению, соответствующие элементы не группируются должным образом. Я предполагаю, что это проблема с запросом, попробовал много комбинаций, но не удался.