典型的「大分類 → 子分類」兩層結構,資料庫通常採用 parent_id 自關聯設計。前端需輸出巢狀 HTML,後端則需將子分類掛載至父分類陣列,再交由模板引擎雙層迴圈渲染。
CREATE TABLE `hcl_sort` (
`sort_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`sort_name` VARCHAR(60) NOT NULL,
`parent_id` INT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (`sort_id`),
KEY `idx_parent` (`parent_id`)
);
// 1. 取得所有頂層分類 (parent_id = 0)
$sql = "SELECT * FROM `hcl_sort` WHERE `parent_id` = 0 ORDER BY `sort_id` DESC";
$sort = $db->getAll($sql);
// 2. 逐筆查詢子分類並掛載
foreach ($sort as $key => $val) {
$pid = $sort[$key]['sort_id'];
$sql = "SELECT * FROM `hcl_sort` WHERE `parent_id` = $pid";
$zi = $db->getAll($sql);
$sort[$key]['zi'] = $zi; // 關鍵:將子分類陣列存入 'zi' 索引
}
$tpl->assign('sort', $sort); // 送入模板引擎 (如 Smarty)
效能備註: 若分類超過百筆,建議改用單次查詢 + PHP 端組樹,或採用遞歸 CTE (MySQL 8.0+/PostgreSQL) 以降低資料庫往返次數。
<div class="modContent">
{foreach from=$sort item=s}
<div class="categories">
<acronym class="icon"></acronym>
<a href="category.php?sort_id={$s.sort_id}">
<strong>{$s.sort_name}</strong>
</a><br/>
{foreach from=$s.zi item=z}
<a href="category.php?sort_id={$z.sort_id}" title="{$z.sort_name}">
{$z.sort_name}
</a>
{/foreach}
</div>
{/foreach}
</div>
模板層僅負責呈現,邏輯完全在 PHP 端完成,符合 MVC 分離原則。
若需三層以上,可改寫遞歸函式:
function buildTree($items, $parentId = 0) {
$branch = [];
foreach ($items as $item) {
if ($item['parent_id'] == $parentId) {
$children = buildTree($items, $item['sort_id']);
if ($children) $item['children'] = $children;
$branch[] = $item;
}
}
return $branch;
}
// 使用方式
$all = $db->getAll("SELECT * FROM hcl_sort ORDER BY sort_id");
$tree = buildTree($all);
$tpl->assign('tree', $tree);
模板端再配合 {function} 遞歸輸出即可支援任意深度。
「兩次查詢 + 陣列掛載 + 雙層迴圈」是中小型專案最划算的實作;大型專案請評估遞歸 CTE 或 Closure Table 模型。無論架構如何,核心皆在於「資料結構轉換」與「模板邏輯極簡化」。