tp6不会自动反序列化json字段,需在模型中声明protected $json = ['config', 'setting'];避免与withattr重复处理,获取器中需手动json_decode。

数据库字段读出来是 JSON 字符串,但代码里直接 foreach 或访问 ->name 就报错?不是框架坏了,是 TP6 默认不自动反序列化 JSON 字段——哪怕你数据库类型设的是 JSON,它也当普通字符串返回。
TP6 模型中 JSON 字段不自动转数组/对象
错误现象:查出来的字段值是带引号的字符串,比如 "{\"name\":\"张三\",\"age\":25}",一 foreach 就提示 Invalid argument supplied for foreach();或者用 $user->config->name 报 Trying to get property 'name' of non-object。
根本原因:TP6 不会因为字段类型是 MySQL 的 JSON 就自动 json_decode,必须显式告诉模型“这个字段存的是 JSON”。
- 在模型类里加
protected $json = ['config', 'setting'];,值必须是数据库字段名(字符串),不能是嵌套路径如'config.name' -
$json只对select()、find()等查询结果生效;写入时 TP 会自动json_encode,无需手动处理 - 如果字段名拼错、大小写不符,或该字段根本不在当前查询结果里(比如用了
field(['id', 'name'])却没包含config),$json就不会触发
和 withAttr 一起用导致重复 decode 或数据变 null
错误现象:字段值变成 null,或者结构异常(比如本该是数组却成了字符串);日志里没报错,但业务逻辑崩了。
典型冲突写法:
protected $json = ['setting']; protected $withAttr = ['setting' => 'json_decode'];
执行流程是:TP 先按 $json 做一次 json_decode → 得到 PHP 数组 → 再进 withAttr,又对这个数组调一次 json_decode → 返回 null。
- 两者机制不同:
$json是模型级声明,走内置 JSON 处理链;withAttr是运行时获取器,优先级更高,会直接接管字段值 - 冲突时推荐只选一种:
$json更轻量、符合 TP 设计意图;withAttr适合需要 fallback、兼容旧格式等定制场景 - 如果必须用
withAttr,记得删掉对应字段的$json声明
toArray() 有转换但 getAttr() 获取器里没生效
错误现象:调用 $model->toArray() 时 JSON 字段正常转成数组,但自定义获取器 getSettingAttr() 里 $value 还是原始字符串。
这是因为 $json 的反序列化发生在模型属性赋值阶段,而 getAttr() 是在取值时才执行,它拿到的是已处理过的值——但前提是没被覆盖。如果你写了 getSettingAttr(),TP 就不会走 $json 流程,而是直接把原始字段值传进来。
- 想在获取器里保持 JSON 解析逻辑,就得自己
json_decode($value, true) - 如果字段可能为空或格式不规范,建议加判断:
is_string($value) && !empty($value)再 decode -
$json对toArray()、toJson()有效,但对getAttr()、setAttr()不自动叠加,这点容易忽略
最常被绕开的点是:以为只要数据库字段类型设为 JSON,TP 就会自动处理;实际上它只认模型里的 $json 声明。字段名拼错、withAttr 和 $json 并存、获取器里没手动 decode——这三个地方出问题,八成就是 JSON 字段报错的根源。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











