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

PHP 無限級分類兩大實作方式:遞迴法與查詢法深度比較

     編輯:PHP 綜合知識

應用場景

電商分類、CMS 欄目、組織架構、評論回覆等層級不固定的樹狀結構,均需無限級分類支援。本文分析兩種主流 PHP 實作:遞迴組裝法與單次查詢組裝法。

資料表結構

CREATE TABLE `category` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(50) NOT NULL,
  `parent_id` INT DEFAULT 0,  -- 0 表示頂層
  `sort` INT DEFAULT 0
);

方法一:遞迴組裝法(經典、直觀)

class CategoryModel {
    // 取得完整樹狀結構
    public function getTree() {
        $data = $this->select(); // SELECT * FROM category ORDER BY sort
        return $this->buildTree($data);
    }

    // 遞迴建構樹
    private function buildTree(array $data, $parentId = 0, $level = 0) {
        static $tree = [];
        foreach ($data as $item) {
            if ($item['parent_id'] == $parentId) {
                $item['level'] = $level;
                $tree[] = $item;
                $this->buildTree($data, $item['id'], $level + 1);
            }
        }
        return $tree;
    }
}

// 使用
$model = new CategoryModel();
$tree = $model->getTree();
// 輸出:扁平化陣列,按層級排序,可直接用 level 縮排顯示

優點

  • 程式碼簡潔,邏輯清晰,符合樹遞迴本質。
  • 一次查詢取得所有資料,資料庫壓力小。
  • 適合資料量較小(< 5000 筆)且頻繁讀取的場景。

缺點

  • PHP 端需遍歷整個陣列多次,資料量大時效能下降。
  • 遞迴深度受限於 xdebug.max_nesting_level(預設 256),極深層級可能溢位。
  • 難以直接支援「只載入某分支」或「分頁載入」。

方法二:單次查詢 + 陣列索引組裝法(高效能、非遞迴)

function buildTreeIterative(array $items) {
    $tree = [];
    $children = [];

    // 1. 建立 id => item 映射,並初始化 children 陣列
    foreach ($items as &$item) {
        $item['children'] = [];
        $children[$item['id']] = &$item; // 使用引用
    }
    unset($item);

    // 2. 單次遍歷掛載子節點
    foreach ($items as &$item) {
        $pid = $item['parent_id'];
        if ($pid == 0) {
            $tree[] = &$item; // 頂層節點
        } elseif (isset($children[$pid])) {
            $children[$pid]['children'][] = &$item; // 掛載到父節點
        }
    }

    return $tree;
}

// 使用
$data = $db->query("SELECT * FROM category ORDER BY sort")->fetchAll(PDO::FETCH_ASSOC);
$tree = buildTreeIterative($data);
// $tree 為多維陣列,每節點含 children 子陣列

優點

  • O(n) 時間複雜度,單次遍歷,無遞迴呼叫開銷。
  • 記憶體效率高,使用引用避免資料複製。
  • 易於擴充:可在 SQL 加 WHERE parent_id IN (...) 只載入特定分支。
  • 無遞迴深度限制。

缺點

  • 邏輯稍抽象,需理解引用機制。
  • 若資料量極大(> 10 萬),單次查詢記憶體壓力大,需分頁或游標。

進階:資料庫層面的遞迴查詢 (CTE, MySQL 8.0+)

WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, 0 AS level, CAST(name AS CHAR(1000)) AS path
  FROM category WHERE parent_id = 0
  UNION ALL
  SELECT c.id, c.name, c.parent_id, ct.level + 1, CONCAT(ct.path, ' > ', c.name)
  FROM category c JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY path;

優點:資料庫直接回傳層級與路徑,應用層無需組裝。缺點:需 MySQL 8.0+,複雜查詢可能較慢。

前端顯示範例(配合遞迴法扁平陣列)

function renderSelect($tree, $selectedId = 0) {
    $html = '';
    return $html;
}

儲存策略對比

策略查詢子孫查詢祖先移動子樹適用場景
Adjacency List (parent_id)需遞迴/CTE簡單簡單通用、寫多讀少
Path Enumeration (path: 1/5/12/)LIKE '1/5/%'簡單需更新路徑讀多、層級固定
Nested Set (lft, rgt)極快極快複雜極少變動、大量讀取
Closure Table (祖先關係表)JOIN 查詢JOIN 查詢批次寫入高頻讀寫、複雜查詢

選型建議

  1. 中小型專案(< 5000 節點):採用 Adjacency List + 遞迴組裝法,開發最快、維護最易。
  2. 大型專案、高頻讀取:採用 Closure Table 或 Path Enumeration,查詢效能最佳。
  3. MySQL 8.0+ 環境:可直接使用 CTE 遞迴查詢,簡化應用層程式碼。
  4. 只需扁平顯示(如下拉選單):遞迴法回傳扁平陣列 最方便。
  5. 需完整樹狀 JSON 輸出 API:迭代法建構多維陣列 結構最自然。

理解各種儲存策略與組裝演算法的優劣,依專案規模與查詢模式選型,才能打造高效能、易維護的無限級分類系統。

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