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

PHP 遞歸獲取分類樹所有子類 ID 完整實現

     編輯:PHP 相關知識

前言

在電商、CMS 等系統中,分類結構通常採用「父子級關聯」設計(如 parent_cid 指向父分類 ID)。本文介紹如何通過遞歸算法高效獲取指定分類下的所有子孫分類 ID,並結合緩存機制優化效能。

數據結構設計

分類表結構範例:

CREATE TABLE item_cat_info (
    cid INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    parent_cid INT DEFAULT 0,
    root_cid INT DEFAULT 0,
    KEY idx_parent (parent_cid)
);

數據樣本:

cid | name       | parent_cid | root_cid
----|------------|------------|---------
1   | 電子產品    | 0          | 0
2   | 手機       | 1          | 1
3   | 蘋果手機    | 2          | 1
4   | 華為手機    | 2          | 1
5   | 電腦       | 1          | 1
6   | 筆記本     | 5          | 1

核心遞歸函數實現

/**
 * 遞歸獲取指定分類下所有子孫分類 ID
 * 
 * @param array  $categories  分類數據陣列 [cid => ['cid', 'parent_cid', ...]]
 * @param int    $parentId    父分類 ID
 * @param int    $level       當前遞歸層級(內部使用)
 * @param bool   $resetCache  是否重置靜態緩存
 * @return array|false        子孫分類 ID 陣列,失敗返回 false
 */
static function getMenuTree(array $categories, int $parentId = 0, int $level = 0, bool $resetCache = true): array|false {
    static $tree = [];
    
    // 重置緩存(首次調用或強制重置時)
    if ($resetCache) {
        $tree = [];
    }
    
    if (empty($categories)) {
        return false;
    }
    
    $level++;
    
    // 第一層記錄根節點
    if ($level === 1) {
        $tree[] = $parentId;
    }
    
    foreach ($categories as $key => $category) {
        if ($category['parent_cid'] == $parentId) {
            $tree[] = $category['cid'];
            
            // 移除已處理節點,減少後續遍歷開銷
            unset($categories[$key]);
            
            // 遞歸處理子分類
            self::getMenuTree($categories, $category['cid'], $level, false);
        }
    }
    
    return $tree;
}

調用範例

// 假設從資料庫獲取所有分類
$allCategories = [
    1 => ['cid' => 1, 'name' => '電子產品', 'parent_cid' => 0, 'root_cid' => 0],
    2 => ['cid' => 2, 'name' => '手機', 'parent_cid' => 1, 'root_cid' => 1],
    3 => ['cid' => 3, 'name' => '蘋果手機', 'parent_cid' => 2, 'root_cid' => 1],
    4 => ['cid' => 4, 'name' => '華為手機', 'parent_cid' => 2, 'root_cid' => 1],
    5 => ['cid' => 5, 'name' => '電腦', 'parent_cid' => 1, 'root_cid' => 1],
    6 => ['cid' => 6, 'name' => '筆記本', 'parent_cid' => 5, 'root_cid' => 1],
];

// 獲取「手機」(cid=2) 下所有子分類
$childIds = getMenuTree($allCategories, 2);
// 結果:[2, 3, 4]  (包含自身 ID 2)

// 獲取頂級分類「電子產品」(cid=1) 下所有子分類
$allChildIds = getMenuTree($allCategories, 1);
// 結果:[1, 2, 3, 4, 5, 6]

緩存優化策略:分類數據文件化

避免每次請求都查詢資料庫,將分類數據導出為 PHP 陣列文件:

public function actionExportCategoryCache(): void {
    $filePath = Yii::getPathOfAlias('application') . '/data/itemcat.php';
    
    $db = Yii::app()->db;
    $rows = $db->createCommand(
        "SELECT cid, name, parent_cid, root_cid FROM item_cat_info"
    )->queryAll();
    
    $categories = [];
    foreach ($rows as $row) {
        $categories[$row['cid']] = [
            'cid' => $row['cid'],
            'name' => $row['name'],
            'parent_cid' => $row['parent_cid'],
            'root_cid' => $row['root_cid'],
        ];
    }
    
    // 生成可直接 require 的 PHP 文件
    $content = "

使用緩存文件調用

$categories = require('/path/to/data/itemcat.php');
$childIds = getMenuTree($categories, $targetParentId);

效能優化建議

  • 引用傳遞優化:大數據量時建議將 $categories 以引用傳遞 &$categories,避免陣列複製開銷
  • 索引預處理:預先建立 parent_cid => [子分類列表] 的映射表,將 O(n²) 降為 O(n)
  • 非遞歸實現:極深層級分類(>1000 層)可能導致棧溢出,可改用堆疊/隊列迭代實現

非遞歸迭代版本(推薦大數據量)

function getMenuTreeIterative(array $categories, int $parentId): array {
    // 建立父子映射索引
    $childrenMap = [];
    foreach ($categories as $cat) {
        $childrenMap[$cat['parent_cid']][] = $cat['cid'];
    }
    
    $result = [$parentId];
    $stack = [$parentId];
    
    while (!empty($stack)) {
        $current = array_pop($stack);
        if (isset($childrenMap[$current])) {
            foreach ($childrenMap[$current] as $childId) {
                $result[] = $childId;
                $stack[] = $childId;
            }
        }
    }
    
    return $result;
}

總結

遞歸獲取分類樹是經典樹形結構遍歷問題。關鍵優化點在於:靜態變量緩存結果、unset 移除已處理節點減少遍歷、配合文件緩存避免重複查庫。對於超大規模或極深層級分類,建議採用非遞歸迭代版本並預建立父子映射索引。

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