ThinkPHP 6 默认不自动反序列化 JSON 字段,需在模型中声明 protected $json = ['config', 'extra']; 才能将数据库 JSON 字符串转为 PHP 数组/对象,否则直接访问会报错;$json 仅对模型查询生效,不支持点号嵌套字段,且与 withAttr 冲突时应避免共用。

ThinkPHP 6 的 json 字段自动转数组/对象失效?
不是框架坏了,是它默认不自动反序列化 json 字段——哪怕你数据库字段类型是 JSON,TP6 仍当它是字符串返回。这导致你直接 foreach 报错、->name 访问报错,本质是没触发反序列化。
解决方式很简单:在模型里显式声明该字段为 json 类型,让 TP 知道“这里存的是 JSON,读出来得给我转成 PHP 结构”:
protected $json = ['config', 'extra'];
注意:$json 是模型属性,不是数据库字段名列表,值必须是字段名(字符串),且仅对 select / find 等查询结果生效;写入时 TP 会自动 json_encode,无需手动处理。
- 如果字段名含点号(如
user.profile),不支持,TP 不解析嵌套路径 -
$json对toArray()有效,但对getAttr()自定义获取器不自动叠加——你要自己在获取器里json_decode - MySQL 5.7+ 的
JSON类型字段和普通TEXT字段在此配置下行为一致,TP 不区分底层存储类型
强转失败报 Cannot use object as array 怎么定位?
典型场景:查出数据后直接 $data->info['id'],但 info 其实还是 JSON 字符串,没被解包。错误不是发生在数据库层,而是在 PHP 运行时。
立即学习“PHP免费学习笔记(深入)”;
快速验证方法:在出错行前加一句 var_dump(gettype($data->info), $data->info);。如果输出 string 和一串带引号的 JSON,说明 $json 没配对,或字段名拼错了。
- 常见拼写错误:
$json = ['Info']但数据库字段是info(大小写敏感) - 使用了
withAttr但忘了删掉$json,两者冲突会导致重复 decode 或跳过处理 - 从
Db::table()直接查,绕过了模型,$json完全不生效——必须用模型实例查询
withAttr 和 $json 能不能一起用?
能,但没必要,而且容易互相覆盖。两者都用于字段类型转换,但机制不同:$json 是模型级全局声明,走 TP 内置 JSON 处理流程;withAttr 是查询时动态绑定的获取器,优先级更高,会直接接管字段值。
如果你写了:
protected $json = ['setting'];<br>protected $withAttr = [<br> 'setting' => 'json_decode'<br>];
结果是:TP 先按 $json 解一次,再进 withAttr 又解一次——第二次 json_decode 作用在数组上,返回 null,数据就丢了。
- 推荐只选一种:
$json更轻量、更符合 TP 设计意图;withAttr适合需要定制逻辑的场景(比如兼容旧格式、带默认值 fallback) - 若必须用
withAttr,请确保字段不在$json列表里,避免双重处理 -
withAttr的回调函数接收原始字符串,返回值将直接作为最终属性值,不经过任何后续转换
MySQL 8.0 的 JSON 函数在 TP 里怎么安全用?
TP 自身不解析 SQL 中的 JSON_EXTRACT、JSON_CONTAINS 等函数返回值,它们只是字符串。也就是说,你在 field 里写 JSON_EXTRACT(data, "$.name") as name,查出来 $data->name 仍是字符串,不是 PHP 数组。
这时候别指望 $json 自动生效——它只对原始字段名起作用,不识别 SQL 别名或表达式。
- 方案一:在查询后手动
json_decode($data->name),适用于少量字段 - 方案二:改用模型的
append属性 + 获取器,在获取器里做json_decode,保持调用一致性 - 方案三:避免在 SQL 层解析 JSON,把结构化提取逻辑移到 PHP,更可控、更易测试
真正麻烦的是嵌套 JSON 查询 + 分页 + 关联预载,这时候 SQL 层解析看似高效,实则让数据边界模糊,调试成本远高于 PHP 层处理。


















