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

PHP 樹狀目錄結構實作:從資料庫到前端樹狀元件

     編輯:PHP 相關知識

樹狀目錄是檔案管理、組織架構、分類導航的基礎 UI。本文示範從資料庫讀取扁平資料,構建樹狀陣列並輸出 HTML 巢狀清單,支援無限層級與前端互動。

資料庫結構

CREATE TABLE categories (
    id INT PRIMARY KEY AUTO_INCREMENT,
    parent_id INT DEFAULT 0 COMMENT '父節點ID,0為根節點',
    name VARCHAR(100) NOT NULL,
    sort_order INT DEFAULT 0,
    is_active TINYINT DEFAULT 1,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

PHP 構建樹狀陣列

<?php
function buildTree(array $items, int $parentId = 0): array {
    $tree = [];
    foreach ($items as $item) {
        if ($item['parent_id'] == $parentId) {
            $children = buildTree($items, $item['id']);
            if ($children) {
                $item['children'] = $children;
            }
            $tree[] = $item;
        }
    }
    return $tree;
}

// 使用範例
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $pdo->query('SELECT * FROM categories WHERE is_active = 1 ORDER BY sort_order, id');
$flatData = $stmt->fetchAll(PDO::FETCH_ASSOC);

$tree = buildTree($flatData);
// $tree 現為巢狀陣列,可直接 json_encode 輸出給前端
?>

輸出 HTML 巢狀清單(含摺疊功能)

<?php
function renderTree(array $tree, int $level = 0): string {
    if (empty($tree)) return '';
    
    $html = '
    '; foreach ($tree as $node) { $hasChildren = !empty($node['children']); $toggleClass = $hasChildren ? 'has-children' : ''; $expanded = $level < 2 ? 'expanded' : ''; // 前兩層預設展開 $html .= '
  • '; $html .= ''; if ($hasChildren) { $html .= ''; } else { $html .= ''; } $html .= '' . htmlspecialchars($node['name']) . ''; $html .= ''; if ($hasChildren) { $html .= renderTree($node['children'], $level + 1); } $html .= '
  • '; } $html .= '
'; return $html; } // 輸出 echo renderTree($tree); ?>

前端互動:CSS + 少量 JavaScript

<style>
.tree-level-0 { margin: 0; padding-left: 0; list-style: none; }
.tree-level-0 > li { margin: 2px 0; }
.tree-node { display: flex; align-items: center; gap: 4px; padding: 4px 8px; cursor: pointer; }
.tree-node:hover { background: #f0f0f0; border-radius: 3px; }
.tree-toggle { width: 16px; height: 16px; border: none; background: none; cursor: pointer; font-size: 10px; }
.tree-spacer { width: 16px; }
.tree-label { flex: 1; }
.has-children > ul { display: block; }
.has-children.collapsed > ul { display: none; }
.has-children.collapsed .tree-toggle::before { content: '▶'; }
.has-children.expanded .tree-toggle::before { content: '▼'; }


<script>
document.addEventListener('click', e => {
    const toggle = e.target.closest('.tree-toggle');
    if (toggle) {
        const li = toggle.closest('li.has-children');
        li.classList.toggle('collapsed');
        li.classList.toggle('expanded');
        toggle.setAttribute('aria-expanded', li.classList.contains('expanded'));
    }
});

大量資料優化:延遲載入

<?php
// 僅載入前 2 層,其餘透過 AJAX 載入
function buildTreePartial(array $items, int $parentId = 0, int $maxDepth = 2, int $currentDepth = 0): array {
    if ($currentDepth >= $maxDepth) return [];
    
    $tree = [];
    foreach ($items as $item) {
        if ($item['parent_id'] == $parentId) {
            $children = buildTreePartial($items, $item['id'], $maxDepth, $currentDepth + 1);
            $item['has_children'] = (bool)($children || hasChildrenInDb($item['id']));
            if ($children) $item['children'] = $children;
            $tree[] = $item;
        }
    }
    return $tree;
}

function hasChildrenInDb(int $parentId): bool {
    global $pdo;
    $stmt = $pdo->prepare('SELECT 1 FROM categories WHERE parent_id = ? AND is_active = 1 LIMIT 1');
    $stmt->execute([$parentId]);
    return (bool)$stmt->fetchColumn();
}
?>

前端 AJAX 載入子節點

<script>
async function loadChildren(nodeId, liElement) {
    if (liElement.dataset.loaded === 'true') return;
    
    const response = await fetch(`/api/categories/children?parent_id=${nodeId}`);
    const children = await response.json();
    
    const ul = document.createElement('ul');
    ul.className = 'tree-level-dynamic';
    ul.innerHTML = renderTreeAjax(children); // 類似 renderTree 但輸出字串
    liElement.appendChild(ul);
    liElement.dataset.loaded = 'true';
}

function renderTreeAjax(children) { /* 同 renderTree 邏輯 */ }

資料庫索引建議

-- 核心查詢索引
CREATE INDEX idx_parent_active ON categories(parent_id, is_active);
CREATE INDEX idx_sort ON categories(parent_id, sort_order, id);

-- 遞迴 CTE 查詢 (MySQL 8.0+) 亦受益於上述索引

樹狀目錄核心在於「扁平資料 ⇄ 樹狀結構」雙向轉換,PHP 端負責構建、前端負責渲染與互動,大量資料採用延遲載入平衡效能與體驗。

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