ThinkPHP 模板引擎的 volist 標簽支持多層嵌套,是輸出樹形結構、分類關聯數據的核心工具。本文通過「分類-報價」實戰案例,完整演示控制器數據構建、模板嵌套寫法、嵌套層級限制配置、常見錯誤排查。
<volist name="list" id="vo">
<volist name="vo['sub']" id="sub">
{$sub.name}
</volist>
</volist>
name:數據源變量 (模板變量名或嵌套鍵路徑)id:循環變量名 (當前項)TAG_NESTED_LEVEL-- 分類表
CREATE TABLE category (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
listorder INT DEFAULT 0
);
-- 報價表
CREATE TABLE quote (
id INT PRIMARY KEY AUTO_INCREMENT,
cate_id INT NOT NULL COMMENT '關聯分類ID',
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2)
);
<?php
namespace Home\Controller;
use Think\Controller;
class IndexController extends Controller {
public function index() {
$Quote = M('Quote');
$Category = M('Category');
// 1. 查詢所有分類 (父級數據)
$categories = $Category->order('listorder')->select();
// 2. 遍歷分類,查詢各分類下的報價,掛載為 'voo' 子數組
foreach ($categories as $key => $cate) {
$categories[$key]['voo'] = $Quote->where(['cate_id' => $cate['id']])->select();
}
// 3. 賦值到模板
$this->assign('list', $categories);
$this->display();
}
}
?>
關鍵點:
volist 遍歷 $list (即 $categories)volist 遍歷 vo['voo'] (控制器掛載的子數組鍵名)voo 可自定義,但控制器與模板必須一致<!-- 分類列表頁 -->
<volist name="list" id="vo">
<div class="category-item">
<h3>{$vo.name}</h3>
<!-- 內層遍歷該分類下的報價 -->
<volist name="vo['voo']" id="sub">
<div class="quote-item">
<span class="name">{$sub.name}</span>
<span class="price">¥{$sub.price}</span>
</div>
</volist>
</div>
</volist>
分類:手機 報價:iPhone 15 Pro - ¥7999 報價:小米 14 Ultra - ¥5999 分類:筆記本 報價:MacBook Pro M3 - ¥14999 報價:ThinkPad X1 Carbon - ¥12999
// config.php 或模板引擎配置 'TAG_NESTED_LEVEL' => 5, // 支持 5 層嵌套
$countries = M('Country')->select();
foreach ($countries as $i => $country) {
$provinces = M('Province')->where(['country_id' => $country['id']])->select();
foreach ($provinces as $j => $province) {
$provinces[$j]['cities'] = M('City')->where(['province_id' => $province['id']])->select();
}
$countries[$i]['provinces'] = $provinces;
}
$this->assign('list', $countries);
<volist name="list" id="country">
{$country.name}
<volist name="country['provinces']" id="province">
{$province.name}
<volist name="province['cities']" id="city">
{$city.name}
</volist>
</volist>
</volist>
上述示例中,foreach 循環查詢會產生 1 + N 條 SQL (N=分類數)。大數據量時優化方案:
$data = M('Category')
->join('LEFT JOIN quote ON category.id = quote.cate_id')
->field('category.id, category.name, quote.id as qid, quote.name as qname, quote.price')
->order('category.listorder, category.id')
->select();
// PHP 重組為樹形
$tree = [];
foreach ($data as $row) {
$cid = $row['id'];
if (!isset($tree[$cid])) {
$tree[$cid] = ['id' => $cid, 'name' => $row['name'], 'voo' => []];
}
if ($row['qid']) {
$tree[$cid]['voo'][] = ['id' => $row['qid'], 'name' => $row['qname'], 'price' => $row['price']];
}
}
$this->assign('list', array_values($tree));
$categories = M('Category')->select();
$quotes = M('Quote')->select();
$quoteMap = [];
foreach ($quotes as $q) {
$quoteMap[$q['cate_id']][] = $q;
}
foreach ($categories as &$cate) {
$cate['voo'] = $quoteMap[$cate['id']] ?? [];
}
$this->assign('list', $categories);
| 錯誤現象 | 原因 | 解決 |
|---|---|---|
| 內層不輸出 / 空白 | 鍵名不匹配:控制器用 voo,模板寫 vo['sub'] | 統一鍵名,或模板改為 vo['voo'] |
| 嵌套超過 3 層報錯 | 默認 TAG_NESTED_LEVEL=3 | 配置文件設置 'TAG_NESTED_LEVEL' => 5 |
| 變量衝突 / 顯示錯亂 | 內外層 id 變量名相同 | 內層用不同 id:id="sub"、id="city" |
在嵌套中使用 <form> 出錯 | HTML 不允許 <form> 嵌套 | 調整結構:每行一個 form、或用 AJAX 提交 |
| 數據量大頁面極慢 | N+1 查詢、未優化 | 使用方案 A/B 優化為 1-2 條 SQL |
<volist name="list" id="vo" key="k" mod="3" empty="暫無數據">
<div class="item-{$vo.id} row-{$k % 3}">{$vo.name}</div>
</volist>
key:索引變量名 (默認 key)mod:取模數,配合 $key % mod 實現隔行換色empty:數據為空時顯示內容offset / length:截取片段assign('list', $data)volist name="list" → 內層 volist name="vo['子鍵名']"TAG_NESTED_LEVELkey、mod、empty 提升模板表達力