栏目自定义字段
内置字段不够用时(交期、是否推荐、规格表、按钮文案…),不需要改表、也不需要改程序:在栏目上定义几个字段,内容编辑页就多几个输入框,模板按字段键直接取值。
一、它是什么:一条数据的三个位置
整套机制只有三块,理解了这三块就不会用错:
① 字段定义 → 表 tw_sort_field
「哪个栏目、有哪些字段、各是什么类型」
一个栏目可以定义任意多个字段(建议 10 个以内)
② 字段的值 → 表 tw_content 的 meta 列(JSON)
{"delivery":"7 天","recommend":"1","spec":"尺寸 20×30mm"}
③ 模板取值 → {$list.字段键} / {$content.字段键}
程序在取数据时自动把 JSON 展开成字段,模板当普通字段用
内置字段(标题、摘要、正文、价格…)是表里真实存在的列,谁都能用;自定义字段是「存在一个 JSON 列里的键值对」,只对定义了它的栏目生效。两者在模板里的写法完全一样,都是 {$list.xxx}。
二、四步做出一个字段
第 1 步 · 定义字段
后台 → 栏目管理 → 找到目标栏目那一行 → 点「字段」按钮(也可以直接访问 /admin.php/sort/fields?sort_id=栏目ID)。
| 项 | 怎么填 |
|---|---|
| 字段名称 | 中文标签,只影响后台显示,如「交期」 |
| 字段键 | 模板里用的变量名,只允许小写字母、数字、下划线,如 delivery。留空会自动生成 cf_ + 拼音 |
| 类型 | 单行文本 / 多行文本 / 下拉选择 / 开关 |
| 选项 | 只有下拉选择要填,一行一个,如「3 天 / 7 天 / 15 天」 |
| 默认值 | 新增内容时预填,可留空 |
保存后立刻生效,不需要清缓存、不需要改代码。要几个字段就重复几次。
第 2 步 · 填值
内容管理 → 添加 / 编辑该栏目下的内容 → 字段输入框自动出现。切换栏目时字段区实时跟着换。
栏目一旦定义了自定义字段,内容编辑页原来那个「扩展」文本框就会隐藏,因为两者共用 meta 列。已经写过的纯文本不会被删,只是不再显示——取消全部字段定义后会重新出现。
第 3 步 · 模板取值
{* 列表页 *}
{tw:list sort="products" limit="12" alias="item"}
<h3>{$item.title}</h3>
<p>交期:{$item.delivery}</p>
{if $item.recommend == '1'}<em>推荐</em>{/if}
{/tw:list}
{* 详情页 *}
<p>交期:{$content.delivery}</p>
第 4 步 · 清缓存看效果
静态化开着时要清一次缓存(后台右上角),否则前台还是旧 HTML。开发期建议先把静态化关掉。
三、四种类型:存什么、怎么写
| 类型 | 后台控件 | 实际存的值 | 模板写法 |
|---|---|---|---|
text |
单行输入框 | 纯文本,超长截断到 500 字 | {$list.键} |
textarea |
多行文本框 | 带换行的纯文本 | {$list.键|nl2br} 才会换行 |
select |
下拉(选项一行一个) | 选中的那行文字,不是序号 | {if $list.键 == '7 天'} |
switch |
开关 | '1' 开 / '0' 关(字符串) |
{if $list.键 == '1'} |
存值就是模板里要比对的值,改选项文字时前台判断也跟着改,不用记序号。代价是改了选项文字,已在用的旧内容不会自动变——要么保持文字稳定,要么事后批量改内容。
四、完整例子:给产品加三个字段
目标:产品详情页显示「交期」「是否推荐」「规格说明」三项。
① 定义(后台操作,等价 SQL 如下)
-- 假设栏目 ID = 10
INSERT INTO `tw_sort_field`
(`sort_id`,`name`,`fkey`,`type`,`options`,`defval`,`sort`,`status`)
VALUES
(10,'交期', 'delivery', 'select','3 天\n7 天\n15 天','7 天',1,1),
(10,'是否推荐','recommend', 'switch','','0', 2,1),
(10,'规格说明','spec', 'textarea','','', 3,1);
② 填值后,meta 列里是这段 JSON
{"delivery":"7 天","recommend":"1","spec":"尺寸 20×30mm\n厚度 5mm\n材质 钕铁硼"}
③ 详情页模板
<div class="spec">
<h2>产品参数</h2>
<dl>
{if $content.delivery}
<dt>交期</dt><dd>{$content.delivery}</dd>
{/if}
<dt>推荐</dt>
<dd>{if $content.recommend == '1'}是{else /}否{/if}</dd>
{if $content.spec}
<dt>规格</dt><dd>{$content.spec|nl2br}</dd>
{/if}
</dl>
</div>
④ 列表页带「推荐」角标
{tw:list sort="products" limit="12" alias="item"}
<a href="{$item.url}" class="card">
{if $item.recommend == '1'}<span class="tag">推荐</span>{/if}
<h3>{$item.title}</h3>
<p>交期 {$item.delivery}</p>
</a>
{/tw:list}
⑤ 想把所有字段一次性铺出来
{* 注意:必须用 foreach,不能用 volist *}
{foreach name="list.cf" item="val" key="fkey"}
<li><b>{$fkey}</b>:{$val}</li>
{/foreach}
字段的中文名不在 cf 里(cf 只有键和值)。要中文标签,最省事的做法是在模板里写死一份映射:
{php}
$labels = ['delivery' => '交期', 'recommend' => '推荐', 'spec' => '规格说明'];
{/php}
{foreach name="list.cf" item="val" key="fkey"}
<li><b>{$labels[$fkey] ?? $fkey}</b>:{$val}</li>
{/foreach}
五、值是怎么被展开的
程序在取列表与详情数据时会解析 meta,规则很简单:
| meta 的内容 | 解析结果 |
|---|---|
合法 JSON 对象,如 {"a":"1"} | 展开成 $row['a'] = '1',同时保留 $row['cf'] = ['a'=>'1'] |
纯文本,如 ¥4800 | 原样保留,$row['meta'] 就是这段文字,cf 为空数组 |
| 空 | 什么都不加,模板取 {$list.键} 得到空串 |
同一个 meta 列既可能装纯文本也可能装 JSON。判断方法:后台内容编辑页里「扩展」框还在不在——在,说明该栏目没定义字段,{$list.meta} 是原文;不在,说明已定义字段,要改用 {$list.字段键},否则前台会输出一整段 JSON。
六、边界与限制
| 限制 | 说明 / 应对 |
|---|---|
| 字段键不能用系统保留名 | title、subtitle、summary、content、meta、url、date、lines、cf、sort 等会被拒绝。换一个名字即可 |
| 同一栏目内字段键唯一 | 不同栏目可以同名,互不影响 |
meta 列上限 1000 字 | 所有字段的值合计要控制在这个长度内。字段多、内容长时改用「正文」或单独建栏目存 |
| 改字段键 = 老值失联 | 值是按旧键名存的,改键名后旧值还在 JSON 里但模板取不到。要么别改,要么批量更新内容 |
| 删字段不删值 | 删掉定义后,JSON 里那段值仍保留;重新用同名键定义即可恢复 |
遍历必须用 foreach | {volist} 的 key 输出的是序号 1、2、3,不是字段键 |
| 停用的字段不回填 | 把字段设为停用后,内容表单不再显示它,但已存的值不会丢 |
七、什么时候该用自定义字段
| 需求 | 推荐做法 |
|---|---|
| 产品要多一个「交期」 | ✅ 自定义字段,最合适 |
| 某一类内容要一段长图文 | 用「正文」,别塞字段(会撞 1000 字上限) |
| 全站都要显示公司电话 | 用站点设置,不是字段 |
| 首页要维护三张卡片 | 建一个隐藏栏目,一张卡一条内容,字段做卡片上的小按钮 |
| 同一个值要在多个栏目共享 | 字段是「栏目私有」的,共享需求改用站点设置或独立栏目 |
把栏目的 status 设为 0,它就不出现在导航和列表里,但仍然能用 {tw:list sort="它的slug"} 取内容。适合首页版块、价格卡、常见问题这类「只给模板取数、不对外当页面」的数据。这类栏目的字段和普通栏目完全一样。
八、排查清单
| 现象 | 原因 |
|---|---|
| 栏目列表里没有「字段」按钮 | 程序版本低于 v1.0.44;或升级时没有执行数据库变更(表 tw_sort_field 不存在) |
| 字段页能打开,但表格是空的 | 这个栏目确实还没定义字段 |
| 内容编辑页没有出现字段输入框 | ① 内容选中的栏目不对;② 字段被设为停用;③ 浏览器缓存,强刷一次 |
模板输出 {"delivery":"7 天"} | 用了 {$list.meta},改成 {$list.delivery} |
| 模板取值为空但后台明明填了 | ① 字段键拼错(大小写、下划线);② 静态化缓存没清;③ 在列表页取了详情才有的东西 |
保存时报 Data too long | meta 超过 1000 字,减少字段或缩短内容 |
字段与内置字段的完整对照见 后台 ↔ 变量对照;标签用法见 标签与函数。