Z-Blog 的 Metas:不建表怎么给文章加自定义字段

· zblog · 阅读 27
摘要

Metas 与真实字段的存储差异、Intro 存错位置的典型坑、列表页批量取值与 JSON 结构化数据的用法。

数据库里没有给你预留字段的位置,但主题又需要「阅读时长」「封面图」「来源链接」这种信息——Z-Blog 的解法是 Metas:一张挂在主表上的键值表。用对了很省事,用错了就是一堆取不出来的空值。

一、Metas 到底存在哪

不新建列,而是用 log_Metas 字段存一段序列化数组。写法有两种,效果一样:

// 单个赋值
$article->Metas->Set('readtime', '8 分钟');

// 按数组写入
$article->Metas->readtime = '8 分钟';

$article->Save();

读取:

$t = $article->Metas->Get('readtime');
// 或
$t = $article->Metas->readtime;

底层是 Metas 类,本质一个带 __get / __set 的容器。没赋过值的键返回 null,不是空字符串。

二、和真正字段的区别

真字段(如 log_Intro)Metas
存储独立列序列化在单列里
能否用于查询排序可以基本不行
模板写法{$article.Intro}{$article.Metas.readtime}
适合存什么高频、需筛选低频、只做展示

这条差异很实用:想让字段参与 where 条件或排序,老老实实加列;只做前台展示,Metas 更划算,不用动表结构,插件卸载也不会留垃圾列。

三、最常踩的坑:Intro 存进 Metas 但主题不读

文章摘要就是典型例子。用 API 或插件写入时传 Metas: {Intro: '...'},数据库里确实能看到这一段,但前台 <meta name="description"> 还是原创截断——因为主题读的是 $article->Intro,对应 log_Intro 列,跟 Metas 里的同名键毫无关系。

排查办法:直接看 zbp_post 表,log_Intro 是空的就说明写错地方了。修正要落到列上:

$article->Intro = '一句话摘要';
$article->Save();

判断标准:模板里写 {$article.XXX} 能取到的,XXX 是对象属性;写 {$article.Metas.XXX} 才是 Metas。两者互不覆盖。

四、在列表页批量取:别一个个查库

循环里对每篇文章调 Metas->Get() 不额外查库(数据已随主记录加载),这点可以放心。但如果你写的是「按某个 Metas 值筛选文章」,那就绕不开全表扫,正确做法是:

  • 数量少(几十条)→ 取出数组后在 PHP 里过滤
  • 数量大且要分页 → 建独立关联表,别硬塞 Metas

五、给分类、标签、用户也能挂

Metas 不是 Post 专属,Category、Tag、Member 都有各自的 Metas 字段。比如给分类加「栏目配色」:

$cate->Metas->Set('color', '#2a6df4');
$cate->Save();

模板侧栏渲染时 {$category.Metas.color} 直接可用。做多站点或栏目差异化样式时,比改主题配置项灵活。

六、JSON 结构也能塞

Metas 存的就是字符串,塞一段 JSON 没问题:

$article->Metas->Set('faq', json_encode([
    ['q' => '问题一', 'a' => '答案一'],
    ['q' => '问题二', 'a' => '答案二'],
], JSON_UNESCAPED_UNICODE));

前台解码后循环输出即可,是做 FAQ Schema、结构化数据的快捷通道,不用额外建表。唯一注意:写入前 json_encode,读出后必须 json_decode,忘了解码就会在前台看到一坨花括号。

小结

三句话:要筛选建列,只展示用 Metas;{$a.Metas.k} 和 {$a.k} 是两条路;空值返回 null 不是空串,判断用 !== null。