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

PHP 遞歸實現無限分類樹構建:從數據庫到前端樹形組件完整流程

     編輯:PHP 相關知識

無限分類(多級分類)是電商、CMS、權限系統核心模型。PHP 遞歸構建樹形結構是經典方案,本文從數據庫設計到前端渲染完整演示。

數據庫設計(邻接表模型)

CREATE TABLE categories (
  id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  pid INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父級 ID,0 為頂級',
  name VARCHAR(50) NOT NULL,
  sort INT UNSIGNED DEFAULT 0,
  status TINYINT DEFAULT 1,
  INDEX idx_pid (pid)
);

數據示例

$items = [
  ['id'=>1,  'pid'=>0, 'name'=>'電子產品'],
  ['id'=>2,  'pid'=>0, 'name'=>'圖書音像'],
  ['id'=>3,  'pid'=>1, 'name'=>'手機通訊'],
  ['id'=>4,  'pid'=>1, 'name'=>'電腦辦公'],
  ['id'=>5,  'pid'=>3, 'name'=>'智能手機'],
  ['id'=>6,  'pid'=>3, 'name'=>'功能機'],
  ['id'=>7,  'pid'=>4, 'name'=>'筆記本'],
  ['id'=>8,  'pid'=>4, 'name'=>'台式機'],
  ['id'=>9,  'pid'=>5, 'name'=>'Android 手機'],
  ['id'=>10, 'pid'=>5, 'name'=>'iOS 手機'],
];

核心遞歸函數:構建樹形數組

function buildTree(array $items, int $pid = 0): array {
    $tree = [];
    foreach ($items as $item) {
        if ($item['pid'] == $pid) {
            // 遞歸獲取子節點
            $children = buildTree($items, $item['id']);
            if ($children) {
                $item['children'] = $children;
            }
            $tree[] = $item;
        }
    }
    return $tree;
}

$tree = buildTree($items);
// 結果:包含 children 遞歸嵌套的樹形數組

優化版:引用索引法(O(n) 時間複雜度)

function buildTreeOptimized(array $items, int $pid = 0): array {
    // 1. 建立 id => item 索引(引用)
    $indexed = [];
    foreach ($items as &$item) {
        $indexed[$item['id']] = &$item;
        $item['children'] = []; // 初始化
    }
    unset($item); // 打破引用

    // 2. 掛載子節點到父節點
    foreach ($indexed as $id => &$item) {
        if ($item['pid'] != 0 && isset($indexed[$item['pid']])) {
            $indexed[$item['pid']]['children'][] = &$item;
        }
    }
    unset($item);

    // 3. 篩選頂級節點
    $tree = [];
    foreach ($indexed as $item) {
        if ($item['pid'] == $pid) {
            $tree[] = $item;
        }
    }
    return $tree;
}
// 避免重複遍歷,大數據量 (萬級+) 顯著提速

前端渲染:Vue 3 遞歸組件示例

<!-- TreeNode.vue -->
<script setup>
defineProps(['node']);
</script>

<template>
  <div class="tree-node">
    <span @click="expanded = !expanded">
      {{ node.name }} ({{ node.id }})
    </span>
    <ul v-if="expanded && node.children?.length">
      <TreeNode v-for="child in node.children" :key="child.id" :node="child" />
    </ul>
  </div>
</template>

<style scoped>
.tree-node > span { cursor: pointer; user-select: none; }
.tree-node ul { margin-left: 20px; border-left: 1px solid #ddd; padding-left: 10px; }
</style>

常見業務擴展

需求實現要點
獲取某節點所有子孫 ID遞歸收集 array_column($tree, 'id') 或 CTE 查詢
獲取節點完整路徑 (麵包屑)遞歸向上查找父節點,或存儲 path 字段 (物化路徑)
移動節點 (修改 pid)檢查目標非自身子孫,事務更新 pid + 所有子孫 path
軟刪除連帶子孫遞歸更新 status=0,或定時任務清理
排序調整拖拽前端傳新順序,批量 UPDATE sort

大數據量替代方案

  • 閉包表: 存儲所有祖先後代關係,查詢子樹 O(1),寫入稍繁瑣
  • 物化路徑: path = '1/3/5/',LIKE 查詢子樹,移動需更新全子樹 path
  • 嵌套集合: lft/rgt 值,查詢極快,移動極慢(需重算全表)
  • PostgreSQL ltree / MySQL 8.0 CTE: 原生遞歸查詢 WITH RECURSIVE

遞歸陷阱與防護

  1. 死循環: 數據臟導致 pid 形成環,加 $visited = [] 檢測
  2. 棧溢出: 層級過深 (PHP 默認無限制但內存有限),改用迭代/隊列
  3. 引用殘留: foreach (... as &$v) 後必須 unset($v)
  4. 性能退化: 遞歸版 O(n²),萬級數據必用索引版 O(n)

選擇合適模型:簡單業務用邻接表+遞歸,高頻讀寫大樹用閉包表/CTE,前端組件化渲染體驗最佳。

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