傳統遞歸查詢在層級較深時效能低下。採用 path 字段存儲從根節點到當前節點的 ID 路徑(如 ,1,3,6,),即可通過單條 SQL 按路徑排序輸出,配合前綴縮進實現無限級分類顯示,完全避免遞歸。
| 字段 | 類型 | 說明 |
|---|---|---|
| id | INT PK | 主鍵 ID |
| fid | INT | 父分類 ID,頂級為 0 |
| name | VARCHAR | 分類名稱 |
| path | VARCHAR | 路徑字串,格式:,父ID,當前ID, |
id | fid | name | path
---|-----|------------|------------
1 | 0 | 分類1 | ,1,
2 | 0 | 分類2 | ,2,
3 | 1 | 分類1-1 | ,1,3,
4 | 1 | 分類1-2 | ,1,4,
5 | 2 | 分類2-1 | ,2,5,
6 | 4 | 分類1-2-1 | ,1,4,6,
<?php
class CategoryMenu {
private $conn;
public function __construct() {
$this->conn = new mysqli("localhost", "root", "password", "dbname");
$this->conn->set_charset("utf8mb4");
}
public function getTree() {
$sql = "SELECT id, name, path FROM category ORDER BY path";
$result = $this->conn->query($sql);
$html = '';
while ($row = $result->fetch_assoc()) {
// 計算層級:path 中逗號數量 - 2 (因為首尾各有一個逗號)
$level = substr_count($row['path'], ',') - 2;
$indent = str_repeat(' ', max(0, $level));
$html .= "{$indent}{$row['name']}<br>";
}
return $html;
}
public function __destruct() {
$this->conn->close();
}
}
// 使用
$menu = new CategoryMenu();
echo $menu->getTree();
path 字段前綴即可批量移動整個分支。新增節點時,path = 父節點path + 新ID + ',';刪除需遞歸清理子節點;修改父級需更新該節點及所有後代的 path。建議封裝為 Model 方法統一維護。