首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

兩款 PHP 無限級分類實戰代碼:遞歸輸出與表格樹狀結構

     編輯:PHP 綜合知識

核心思路:父子關係建模

所有無限級分類的本質都是「id + parent_id」雙欄位:

  • 頂級分類:parent_id = 0
  • 子分類:parent_id = 父分類 id
  • 以此類推,無層級上限。

方案一:純遞歸輸出層級縮進

適用於下拉選單、樹形選單、站點地圖等場景。

// 資料庫連接略
function displayTree($prefix, $parentId) {
    $sql = "SELECT * FROM `ylmf_class` WHERE `parentid` = '$parentId'";
    $res = mysql_query($sql);
    while ($row = mysql_fetch_assoc($res)) {
        echo $prefix . $row['classname'] . "
"; // 遞歸輸出子節點,縮進符號疊加 displayTree($prefix . "─┴", $row['id']); } } // 呼叫:從頂級開始 displayTree("├", 0);

輸出效果:

├一級分類A
├─┴二級分類A
├─┴二級分類B
├一級分類B

方案二:TreeTable 類庫——表格式樹狀結構

當需要在後台列表中直觀展示「所屬關係」並支援行合併(rowspan)與列合併(colspan)時,純遞歸輸出 HTML <table> 會非常麻煩。TreeTable 類將數據預處理為座標系,再一次性輸出標準表格。

1. 準備數據陣列

$data = [
    1 => ['id'=>1, 'parentid'=>0, 'name'=>'一級欄目一'],
    2 => ['id'=>2, 'parentid'=>0, 'name'=>'一級欄目二'],
    3 => ['id'=>3, 'parentid'=>1, 'name'=>'二級欄目一'],
    4 => ['id'=>4, 'parentid'=>1, 'name'=>'二級欄目二'],
    5 => ['id'=>5, 'parentid'=>2, 'name'=>'二級欄目三'],
    6 => ['id'=>6, 'parentid'=>3, 'name'=>'三級欄目一'],
    7 => ['id'=>7, 'parentid'=>3, 'name'=>'三級欄目二'],
];

2. 核心類 TreeTable(精簡版)

class TreeTable {
    public $arr = [];      // 節點數據
    public $columns = 0;   // 最大層級數(列數)
    public $rows = 0;      // 底層節點數(行數)

    public function init(array $data) {
        // 1. 以 id 為鍵重組
        foreach ($data as $v) $this->arr[$v['id']] = $v;

        // 2. 計算每節點層級、所有子孫、所有祖先、底層葉子數
        foreach ($this->arr as $k => $v) {
            $this->arr[$k]['column']       = $this->getLevel($v['id']);      // Y 軸:層級
            $this->arr[$k]['arrchildid']   = $this->getArrChildId($v['id']); // 所有子孫 ID 串
            $this->arr[$k]['arrparentid']  = $this->getArrParentId($v['id']); // 所有祖先 ID 串
            $this->arr[$k]['child_bottom_num'] = $this->getChildCount($v['id']); // 葉子數
        }

        $this->columns = $this->getColumns();
        $this->rows    = $this->getRows();

        // 3. 按祖先路徑 + id 排序,保證同層兄弟相鄰
        $this->sortArr();

        // 4. 計算每節點在表格中的行號、rowspan、colspan
        foreach ($this->arr as $k => $v) {
            $this->arr[$k]['row']    = $this->getRowLocation($v['id']);
            $this->arr[$k]['rowspan'] = $v['child_bottom_num'];
            $this->arr[$k]['colspan'] = $v['child_bottom_num'] == 0
                ? $this->columns - $v['column'] + 1 : 0;
        }
        return $this->arr;
    }

    // 以下為內部輔助方法:getLevel、getChildCount、getArrChildId、getArrParentId、getRowLocation、getRows、getColumns、sortArr
    // (完整代碼見原文,此處省略以節省篇幅)

    public function getTreTable() {
        $html = '';
        for ($r = 1; $r <= $this->rows; $r++) {
            $html .= "";
            foreach ($this->arr as $v) {
                if ($v['row'] == $r) {
                    $rs = $v['rowspan'] ? "rowspan='{$v['rowspan']}'" : '';
                    $cs = $v['colspan'] ? "colspan='{$v['colspan']}'" : '';
                    $html .= "{$v['name']}";
                }
            }
            $html .= "";
        }
        return $html;
    }
}

3. 使用範例

$tree = new TreeTable();
$tree->init($data);
echo "";
echo $tree->getTreTable();
echo "
";

渲染效果: 表格第一列顯示一級欄目,第二列顯示二級,第三列顯示三級;同一父節點的所有子孫自動合併儲存格,視覺上極其清晰。

兩種方案選型指南

場景推薦方案理由
前端下拉選單、樹形選擇器方案一(遞歸輸出)代碼極簡,配合 <optgroup> 或 JS 樹插件即可
後台列表頁、Excel 匯出、需行合併展示方案二自動計算 rowspan/colspan,輸出標準 HTML 表格,零前端邏輯
數據量 > 5000 節點兩者皆不建議改用 Closure Table 或資料庫遞歸 CTE(MySQL 8.0+)

效能優化提示

  • 一次性 SELECT * FROM table 讀入記憶體,遞歸在 PHP 陣列中完成,避免 N+1 查詢。
  • 若頻繁讀取,將構建好的 $tree->init($data) 結果序列化存入 Redis,下次直接反序列化輸出。
  • 修改分類時,只需更新 parent_id,重建緩存即可,無需像 rank 方案那樣級聯更新路徑字段。

根據業務場景靈活選擇,即可輕鬆駕馭任何層級的分類展示需求。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved