ThinkPHP 社群常討論無限分類實作,許多教學採用「路徑法(0-1-2-3)」或「層級法」。本文分享一種以遞迴實作的簡潔做法,程式碼不超過 20 行,邏輯清晰易維護。
單表 channel 即可支援無限分層:
| 欄位 | 說明 |
|---|---|
| cid | 主鍵 ID |
| fatherid | 父分類識別(頂層為 NULL) |
| name | 分類名稱 |
註:頂層分類的 fatherid 設為 NULL 而非 0,配合外鍵關聯模型設計。
class CategoryAction extends Action {
// 類屬性:儲存遞迴結果
public $tree = null;
// 測試輸出
public function test() {
// 取得所有頂層分類
$res = M('channel')->where('fatherid IS NULL')->select();
$this->createtree($res);
dump($this->tree);
}
// 遞迴方法
private function createtree(array $data = null, $lv = 1) {
for ($i = 0; $i < count($data); $i++) {
// 寫入層級
$data[$i]['lv'] = $lv;
// 將當前記錄加入結果陣列末尾
$this->tree[count($this->tree)] = $data[$i];
// 查詢子分類
$res = M('channel')->where('fatherid=' . $data[$i]['cid'])->select();
// 遞迴處理子分類
$this->createtree($res, $lv + 1);
}
}
}
fatherid IS NULL 的頂層分類。lv。$this->tree 陣列末尾(保證父分類在子分類之前)。+1。for 迴圈若查無子分類($res 為空),自然結束遞迴,無需額外判斷。實務上分類數量極少超過 200 筆,遞迴查詢效能足夠。若極度在意效能,可採用快取機制(如 Redis、檔案快取),分類異動時更新快取,讀取時直接取用,避免重複查詢。
若需自訂分類順序,直接在 select() 前加上 order('sort_field ASC'),不影響遞迴邏輯。
原文出處:尛雷博客