實現無限分類(無限級分類)的關鍵在於資料庫表結構的設計。最經典且通用的做法是:在分類表中新增一個 parent_id(或 f_id)欄位,用於儲存該分類父級分類的 ID。頂層分類的 parent_id 設為 0,表示無父級。這樣,透過「子查父」或「父查子」的邏輯,即可構建出任意深度的樹狀結構。
CREATE TABLE `class` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '分類ID',
`f_id` int(11) NOT NULL DEFAULT 0 COMMENT '父級ID',
`name` varchar(25) NOT NULL COMMENT '分類名稱',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
插入新分類時,只需指定其父級分類的 ID 作為 f_id:
INSERT INTO class (f_id, name) VALUES (0, '新聞');INSERT INTO class (f_id, name) VALUES (1, 'PHP新聞');INSERT INTO class (f_id, name) VALUES (2, 'PHP 6.0 釋出');同理,可無限向下擴展,亦可在同一層級新增多個分類(f_id 相同)。
若分類層級不固定,寫死多層迴圈顯然不可行。解決方案是編寫一個遞歸函式,接收 parent_id 參數,查詢該父級下的所有子分類,並對每個子分類再次呼叫自身。
為避免遞歸中重複查詢資料庫,建議先將所有分類一次性讀取到二維陣列中,再在記憶體中進行遞歸運算,大幅提升效能。
<?php
// 連線資料庫(建議使用 PDO 或 mysqli 預處理語句)
$db = new mysqli("localhost", "root", "", "news_db");
$db->set_charset("utf8mb4");
// 一次性取出所有分類
$result = $db->query("SELECT id, f_id, name FROM class");
$arr = [];
while ($row = $result->fetch_assoc()) {
$arr[] = [$row['id'], $row['f_id'], $row['name']];
}
function fenlei($f_id = 0) {
global $arr;
foreach ($arr as $item) {
if ($item[1] == $f_id) {
echo $item[2] . "<br>"; // 輸出分類名稱
fenlei($item[0]); // 遞歸處理子分類
}
}
}
// 從頂層開始輸出
fenlei(0);
?>
以下 Tree 類別封裝了更完善的邏輯,能輸出帶有樹狀連線符號(│、├、└)的結構化陣列,便於前端直接渲染:
<?php
class Tree {
var $arr = []; // 來源資料:id 為鍵,值為 ['id', 'parentid', 'name']
var $tree = []; // 輸出結果
var $deep = 1; // 當前遞歸深度
var $icon = ['│', '├', '└']; // 樹狀修飾符號
// 設定來源資料
function setArr($arr) { $this->arr = $arr; }
// 取得指定父級的直系子分類
function getChild($root = 0) {
$child = [];
foreach ($this->arr as $id => $a) {
if ($a['parentid'] == $root) $child[$a['id']] = $a;
}
return $child ?: false;
}
// 核心遞歸方法
function getTree($rootid = 0, $add = '', $parent_end = true) {
$child_arr = $this->getChild($rootid);
if (is_array($child_arr)) {
$cnt = count($child_arr);
$is_top = 1;
foreach ($child_arr as $key => $child) {
$cid = $child['id'];
$space = '';
if ($this->deep > 1) {
if ($is_top == 1) {
$space = $this->icon[1];
$add .= $parent_end ? ' ' : $this->icon[0];
}
if ($is_top == $cnt) {
$space = $this->icon[2];
$parent_end = true;
} else {
$space = $this->icon[1];
$parent_end = false;
}
}
$this->tree[] = [
'spacer' => $add . $space,
'name' => $child['name'],
'id' => $cid
];
$is_top++;
$this->deep++;
if ($this->getChild($cid)) $this->getTree($cid, $add, $parent_end);
$this->deep--;
}
}
return $this->tree;
}
}
// 使用範例
$data = [
1 => ['id'=>1, 'parentid'=>0, 'name'=>'一級欄目一'],
2 => ['id'=>2, 'parentid'=>0, 'name'=>'一級欄目二'],
3 => ['id'=>3, 'parentid'=>1, 'name'=>'二級欄目一'],
];
$tree = new Tree();
$tree->setArr($data);
$result = $tree->getTree();
print_r($result);
?>
f_id 欄位建立索引,加速「查找某父級下的子分類」操作。無限分類的核心在於「父級 ID 關聯」與「遞歸遍歷」。採用「一次查詢 + 記憶體遞歸」的模式,配合封裝良好的樹狀類別,即可高效、靈活地實現多級分類的儲存、查詢與顯示。