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

PHP 無限分類(樹形結構)高性能實現與 Smarty 模板遞歸渲染

     編輯:PHP 綜合知識

核心思路:鄰接表模型 + 內存構建樹

數據庫存儲 id, pid, name, ...,一次查詢取出所有數據,PHP 內存中構建樹結構,避免遞歸查詢數據庫(N+1 問題)。

Tree 類完整實現

class Tree {
    private $data = [];
    private $tree = [];
    private $fields = ['id', 'pid'];
    private $rootPid = 0;

    public function __construct(array $data, array $fields = ['id', 'pid'], $rootPid = 0) {
        $this->data = $data;
        $this->fields = $fields;
        $this->rootPid = $rootPid;
        $this->build();
    }

    private function build() {
        // 1. 按 pid 分組
        $grouped = [];
        foreach ($this->data as $node) {
            $pid = $node[$this->fields[1]];
            $grouped[$pid][] = $node;
        }
        krsort($grouped); // 確保父節點先處理(可選)

        // 2. 遞歸構建子樹
        $this->tree = $this->buildBranch($this->rootPid, $grouped);
    }

    private function buildBranch($pid, &$grouped) {
        $branch = [];
        if (empty($grouped[$pid])) return $branch;
        
        foreach ($grouped[$pid] as $node) {
            $child = $this->buildBranch($node[$this->fields[0]], $grouped);
            if ($child) $node['children'] = $child;
            $branch[] = $node;
        }
        return $branch;
    }

    /** 獲取某節點下的完整子樹(多維數組) */
    public function getBranch($id = null) {
        $id = $id ?? $this->rootPid;
        // 需要先建立 id=>node 映射或遍歷查找,此處簡化
        return $this->findBranch($this->tree, $id);
    }

    private function findBranch($branch, $id) {
        foreach ($branch as $node) {
            if ($node[$this->fields[0]] == $id) return $node['children'] ?? [];
            if (!empty($node['children'])) {
                $res = $this->findBranch($node['children'], $id);
                if ($res) return $res;
            }
        }
        return [];
    }

    /** 獲取從根到某節點的路徑(導航麵包屑) */
    public function getPath($id) {
        $path = [];
        $this->buildPath($this->data, $id, $path);
        return array_reverse($path);
    }

    private function buildPath($data, $id, &$path) {
        foreach ($data as $node) {
            if ($node[$this->fields[0]] == $id) {
                $path[] = $node;
                if ($node[$this->fields[1]] != $this->rootPid) {
                    $this->buildPath($data, $node[$this->fields[1]], $path);
                }
                break;
            }
        }
    }

    /** 獲取某節點下所有後代 ID(含自身) */
    public function getAllDescendantIds($id) {
        $ids = [$id];
        $branch = $this->getBranch($id);
        $this->collectIds($branch, $ids);
        return $ids;
    }

    private function collectIds($branch, &$ids) {
        foreach ($branch as $node) {
            $ids[] = $node[$this->fields[0]];
            if (!empty($node['children'])) $this->collectIds($node['children'], $ids);
        }
    }
}

使用示例

$rawData = [
    ['id'=>1, 'pid'=>0, 'name'=>'首頁'],
    ['id'=>2, 'pid'=>0, 'name'=>'新聞'],
    ['id'=>6, 'pid'=>2, 'name'=>'國內新聞'],
    ['id'=>8, 'pid'=>6, 'name'=>'北京新聞'],
    // ...
];

$tree = new Tree($rawData);

// 1. 完整樹(用於側邊欄菜單)
$menuTree = $tree->getBranch();

// 2. 麵包屑導航(當前分類 ID=8)
$breadcrumb = $tree->getPath(8);

// 3. 獲取分類 2 及所有子分類 ID(用於文章查詢 WHERE cat_id IN (...))
$catIds = $tree->getAllDescendantIds(2);

Smarty 模板遞歸渲染

{* 模板文件 menu.tpl *}
{function name=renderMenu data=$menu}
    
    {foreach $data as $node}
  • {$node.name} {if isset($node.children)} {call name=renderMenu data=$node.children} {/if}
  • {/foreach}
{/function} {* 調用 *} {call name=renderMenu data=$menuTree}

性能優化建議

  • 緩存樹結構:數據變更頻率低時,將構建好的 $tree 序列化存入 Redis/文件,命中緩存直接反序列化。
  • 大數據量分頁/懶加載:前端樹組件(如 zTree、jstree)按需 AJAX 載入子節點,後端提供 getChildren($pid) 接口。
  • 物化路徑:數據庫增加 path 字段存儲 0/2/6/8/,查詢子樹變 WHERE path LIKE '0/2/%',極快。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved