将修改后的预序树遍历模型(嵌套集)转换为<ul>

2022-08-30 19:27:27

我正在尝试将使用树遍历模型分层设置的数据放入<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>

谁能告诉我为什么或如何以分层结构输出列表?

相关主题


答案 1

好吧,让我们做一些赏金狩猎;)

步骤 0 - 清理示例:
如前所述,您的示例数据已损坏,因为它未定义有效的嵌套集。如果从应用获取此数据,则应检查插入/删除逻辑。

因此,为了进行测试,我使用了一个经过消毒的版本,如下所示:
(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:提问者明确要求,但有无序列表!?加油。。。<ul>
;-)


答案 2

更好的渲染树函数,它对我有用(php函数准备在jsTree jQuery插件中使用的html源代码),而不是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>

推荐