【问题标题】:Category Hierarchy (PHP/MySQL)类别层次结构 (PHP/MySQL)
【发布时间】:2011-05-26 01:06:55
【问题描述】:

我正在尝试以层次结构从 MySQL 数据库中获取我的所有类别和子类别:

我的结果应该是这样的(只是例子):

  1. 猫 A
    • 子类 1
      • Sub_Sub_Cat 1
      • Sub_Sub_Cat 2
    • Sub_Cat 2
  2. 猫 B
  3. 猫 C
  4. ...

MySQL 代码:

CREATE TABLE IF NOT EXISTS `categories` (
   `category_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
   `parent_id` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'for sub-categories'
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ;

简单地说,如何使用 PHP 代码 将其置于层次结构中?

【问题讨论】:

    标签: php mysql hierarchy categories


    【解决方案1】:

    使用邻接表模型时,可以一次性生成结构。

    取自One Pass Parent-Child Array Structure (Sep 2007; by Nate Weiner)

    $refs = array();
    $list = array();
    
    $sql = "SELECT item_id, parent_id, name FROM items ORDER BY name";
    
    /** @var $pdo \PDO */
    $result = $pdo->query($sql);
    
    foreach ($result as $row)
    {
        $ref = & $refs[$row['item_id']];
    
        $ref['parent_id'] = $row['parent_id'];
        $ref['name']      = $row['name'];
    
        if ($row['parent_id'] == 0)
        {
            $list[$row['item_id']] = & $ref;
        }
        else
        {
            $refs[$row['parent_id']]['children'][$row['item_id']] = & $ref;
        }
    }
    

    从链接的文章中,这是一个用于创建输出列表的 sn-p。它是递归的,如果一个节点有子节点,它会再次调用自己来构建子树。

    function toUL(array $array)
    {
        $html = '<ul>' . PHP_EOL;
    
        foreach ($array as $value)
        {
            $html .= '<li>' . $value['name'];
            if (!empty($value['children']))
            {
                $html .= toUL($value['children']);
            }
            $html .= '</li>' . PHP_EOL;
        }
    
        $html .= '</ul>' . PHP_EOL;
    
        return $html;
    }
    

    相关问题:

    【讨论】:

    • +1 令人惊讶的是,第一部分完全符合我的需要,而且比我认为的要简单得多。
    • 这是我意识到的非常聪明的想法。我有兴趣将此数组转换为 JSON 数组,但没有 ID 作为索引。这也可能吗?
    • 我在已经动态创建的菜单中使用它时遇到问题。我需要摆脱第一个生成的 ul。
    • 我通过引用开始和结束 UL 并关闭其中的 toUL 来修复它。比如:$html .= '
        '.toUL($v['children'])。 '
      ';
    • 什么应该作为数组传递给 toUL() 方法? $refs 或 $list 或 $row??
    【解决方案2】:

    我有一个新想法,我认为它会很好。 这个想法是这样的: 在 category_parent 列中,我们将插入对该节点所有父节点的引用。

    +----+----------------------+------------------+ |编号 |类别名称 |层次结构 | +----+----------------------+------------------+ | 1 |猫1 | 1 | +----+----------------------+------------------+ | 2 |猫2 | 2 | +----+----------------------+------------------+ | 3 |三类 | 3 | +----+----------------------+------------------+ | 4 |子目录1_1 | 1-4 | +----+----------------------+------------------+ | 5 |子目录1_2 | 1-5 | +----+----------------------+------------------+ | 6 | subsubcat1_1 | 1-4-6 | +----+----------------------+------------------+ | 7 | subsubcat1_2 | 1-4-7 | +----+----------------------+------------------+ | 8 | subsubcat1_3 | 1-4-8 | +----+----------------------+------------------+ | 9 |子目录1_3_1 | 1-4-8-9 | +----+----------------------+------------------+ | 10 | subsubcat1_3_2 | 1-4-8-10 | +----+----------------------+------------------+ | 11 |子分类1_3_1_1 | 1-4-8-9-11 | +----+----------------------+------------------+ | 12 |子subsubcat1_3_1_1 | 1-4-8-9-12 | +----+----------------------+------------------+ | 13 |子subsubcat1_3_1_2 | 1-4-8-9-11-13 | +----+----------------------+------------------+ | 14 | subsubsubcat1_2_1_3 | 1-4-8-9-11-14 | +----+----------------------+------------------+

    如果您查看我更新的表格,您会注意到每条记录都有一个到其父级的链接,不仅是直接的,还包括所有的父级。 对于那项工作,我做了一些修改以插入:

    Insert into table_name (category_name, hierarchy) values ('new_name', (concat(parent_hierarch, '-', (SELECT Auto_increment FROM information_schema.tables WHERE table_name='table_name'))))
    

    现在让我们进行您想要的查询:

    1- 汽车的所有子类别:

    select * from table_name where hierarchy like '1-%'
    

    2- 如果您需要 BLACK 的所有父级,只需键入:

    select * from table_name where hierarchy = '1-4-8-9' or hierarchy = '1-4-8' or hierarchy = '1-4' or hierarchy = '1'
    

    (您可以从 php 构建该查询,在 '-' char 处拆分层次结构字段)

    3- 要查看所有类别,包括级别和直接父级:

    select *, SUBSTR(hierarchy, 1, (LENGTH(hierarchy) - LENGTH(id) - 1)) as parent, LENGTH(hierarchy) - LENGTH(REPLACE(hierarchy, '-', '')) as level From table_name
    
    +----+----------------------------------+-----------------+--- --------+--------+ |编号 |类别名称 |层次结构 |家长 |水平 | +----+----------------------------------+-----------------+--- --------+--------+ | 1 |猫1 | 1 | | 0 | +----+----------------------------------+-----------------+--- --------+--------+ | 2 |猫2 | 2 | | 0 | +----+----------------------------------+-----------------+--- --------+--------+ | 3 |三类 | 3 | | 0 | +----+----------------------------------+-----------------+--- --------+--------+ | 4 |子目录1_1 | 1-4 | 1 | 1 | +----+----------------------------------+-----------------+--- --------+--------+ | 5 |子目录1_2 | 1-5 | 1 | 1 | +----+----------------------------------+-----------------+--- --------+--------+ | 6 | subsubcat1_1 | 1-4-6 | 1-4 | 2 | +----+----------------------------------+-----------------+--- --------+--------+ | 7 | subsubcat1_2 | 1-4-7 | 1-4 | 2 | +----+----------------------------------+-----------------+--- --------+--------+ | 8 | subsubcat1_3 | 1-4-8 | 1-4 | 2 | +----+----------------------------------+-----------------+--- --------+--------+ | 9 |子目录1_3_1 | 1-4-8-9 | 1-4-8 | 3 | +----+----------------------------------+-----------------+--- --------+--------+ | 10 | subsubcat1_3_2 | 1-4-8-10 | 1-4-8 | 3 | +----+----------------------------------+-----------------+--- --------+--------+ | 11 |子分类1_3_1_1 | 1-4-8-9-11 | 1-4-8-9 | 4 | +----+----------------------------------+-----------------+--- --------+--------+ | 12 |子subsubcat1_3_1_1 | 1-4-8-9-12 | 1-4-8-9 | 4 | +----+----------------------------------+-----------------+--- --------+--------+ | 13 |子subsubcat1_3_1_2 | 1-4-8-9-11-13 |1-4-8-9-11 | 5 | +----+----------------------------------+-----------------+--- --------+--------+ | 14 | subsubsubcat1_2_1_3 | 1-4-8-9-11-14 |1-4-8-9-11 | 5 | +----+----------------------------------+-----------------+--- --------+--------+

    这是一个新想法,需要改进。

    【讨论】:

    • 但是,如果您必须在层次结构中进行更改,例如将subcat1_2 转换为subcat3_2,或者删除一个类别,该怎么办?
    【解决方案3】:

    试试下面的代码

    //连接mysql并选择db

    $conn = mysqli_connect('localhost', 'user', 'password','database');

    if( !empty($conn->connect_errno)) die("Error " . mysqli_error($conn));
    
    //call the recursive function to print category listing
    category_tree(0);
    
    //Recursive php function
    function category_tree($catid){
    global $conn;
    
    $sql = "select * from category where parent_id ='".$catid."'";
    $result = $conn->query($sql);
    
    while($row = mysqli_fetch_object($result)):
    $i = 0;
    if ($i == 0) echo '<ul>';
     echo '<li>' . $row->cat_name;
     category_tree($row->id);
     echo '</li>';
    $i++;
     if ($i > 0) echo '</ul>';
    endwhile;
    }
    //close the connection
    mysqli_close($conn);
    ?>
    

    More...

    【讨论】:

    • #Waruna Manjula Plus 1 为您的解决方案伙伴!我把它变成了下拉菜单,但我遇到了将父类别设置为选中的问题。我怎样才能做到这一点?我知道如何设置选定的当前类别,但我需要显示选定的父类别..
    【解决方案4】:

    @Amnon 您的代码运行良好。刚刚用 CodeIgniter 对其进行了测试,它就像一个魅力。如果有人需要,这是工作代码:

    <?php
    
    function disTree($all_cats) {
    $tree = array();
    foreach ($all_cats as $cat)
    {
        $pid  = $cat->parent_id;
        $id   = $cat->cat_id;
        $name = $cat->cat_name;
    
        // Create or add child information to the parent node
        if (isset($tree[$pid]))
            // a node for the parent exists
            // add another child id to this parent
            $tree[$pid]["children"][] = $id;
        else
            // create the first child to this parent
            $tree[$pid] = array("children"=>array($id));
    
        // Create or add name information for current node
        if (isset($tree[$id]))
            // a node for the id exists:
            // set the name of current node
            $tree[$id]["name"] = $name;
        else
            // create the current node and give it a name
            $tree[$id] = array( "name"=>$name );
    }
    return $tree;
    }
    
    
    function toUL($tree, $id, $html){
      $html .= '<ul>'.PHP_EOL;
    
      if (isset($tree[$id]['name']))
        $html .= '<li>' . $tree[$id]['name'];
    
      if (isset($tree[$id]['children']))
      {
        $arChildren = &$tree[$id]['children'];
        $len = count($arChildren);
        for ($i=0; $i<$len; $i++) {
            $html .= toUL($tree, $arChildren[$i], "");
        }
        $html .= '</li>'.PHP_EOL;
      }
    
      $html .= '</ul>'.PHP_EOL;
      return $html;
    }
    
    $tree = disTree($all_cats);
    // Display the tree
    echo toUL($tree, 0, "");
    
    ?>
    

    我唯一改变的是添加我自己的数组 ($all_cats)。

    【讨论】:

    • 谢谢。你让它变得更干净,更容易根据自己的需要定制,也更容易理解。谢谢!我用这个类似的代码坐了几个小时,终于让它工作了。
    【解决方案5】:

    还有另一种实现相同效果的方法,我发现它更容易理解(没有参考技巧)。您可以通过将相关信息添加到当前节点及其父节点来构建树(假设 foreach 遍历 SQL 查询返回的行):

    $tree = array();
    foreach ($query->result() as $row)
    {
        $pid  = $row->parent_id;
        $id   = $row->id;
        $name = $row->name;
    
        // Create or add child information to the parent node
        if (isset($tree[$pid]))
            // a node for the parent exists
            // add another child id to this parent
            $tree[$pid]["children"][] = $id;
        else
            // create the first child to this parent
            $tree[$pid] = array("children"=>array($id));
    
        // Create or add name information for current node
        if (isset($tree[$id]))
            // a node for the id exists:
            // set the name of current node
            $tree[$id]["name"] = $name;
        else
            // create the current node and give it a name
            $tree[$id] = array( "name"=>$name );
    }
    return $tree;
    

    并显示树:

    function toUL($tree, $id, $html){
      $html .= '<ul>'.PHP_EOL;
    
      if (isset($tree[$id]['name']))
        $html .= '<li>' . $tree[$id]['name'];
    
      if (isset($tree[$id]['children']))
      {
        $arChildren = &$tree[$id]['children'];
        $len = count($arChildren);
        for ($i=0; $i<$len; $i++) {
            $html .= toUL($tree, $arChildren[$i], "");
        }
        $html .= '</li>'.PHP_EOL;
      }
    
      $html .= '</ul>'.PHP_EOL;
      return $html;
    }
    
    // Display the tree
    echo toUL($tree, 0, "");
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多