ThinkPHP默认不自动处理JSON字段,需在模型中声明$json和$jsonType属性;TP6必须显式配置,TP8虽默认解码但行为不一致,推荐统一使用访问器控制。

ThinkPHP模型里JSON字段不自动转数组?先看json类型字段是否真被识别
ThinkPHP 6.x 默认不会对数据库字段类型为 json 的字段做自动序列化/反序列化,哪怕你用的是 MySQL 5.7+ 或 PostgreSQL。它只把值当字符串读进来,toArray() 后还是字符串,不是数组。
- 确认数据库字段类型是
json(MySQL)或jsonb/json(PostgreSQL),不是text或varchar - 检查模型类是否继承
thinkModel(TP6)或thinkmodelBase(TP8),旧版手动继承thinkModel才有基础 JSON 支持 - TP6 中需在模型中显式声明
protected $json = ['extra', 'config'];,否则字段值始终为字符串
怎么让extra字段读取时自动json_decode、写入时自动json_encode?
靠模型的 $json 属性 + $jsonType 配合控制行为,不是靠数据库驱动自动处理。
-
protected $json = ['extra'];:声明该字段需要 JSON 编解码(读取时json_decode,写入时json_encode) -
protected $jsonType = ['extra' => 'array'];:指定解码目标类型,可选array/object/string(后者等价于不 decode) - 如果字段名含点号(如
settings.theme),TP 不支持嵌套字段声明,只能手动处理或改用getExtraAttr访问器 - 注意:
$json声明后,$model->extra是数组,但$model->getData('extra')返回原始字符串(未 decode 的原始值)
save()时JSON字段被转成字符串再存?检查setAttr和data传入方式
常见错误是手动调用 json_encode 后再赋值,导致双重编码:"{"name":"a"}" → 写库变成 ""{\"name\":\"a\"}""。
- 直接赋值数组即可:
$model->extra = ['name' => 'a']; $model->save();,TP 会自动 encode - 避免
$model->extra = json_encode(['name' => 'a']);,这会让 TP 再 encode 一次 - 用
data()批量赋值时,同样传原生数组:$model->data(['extra' => ['name' => 'a']])->save(); - 如果用了
allowField(true)且 POST 数据里extra是字符串(如前端传了 JSON 字符串),需先json_decode($input['extra'], true)再塞进 data
TP6 和 TP8 在 JSON 字段处理上的关键差异
TP8 把 JSON 处理逻辑下放到数据库查询构建器层,模型层更“薄”,但默认行为反而更容易踩坑。
立即学习“PHP免费学习笔记(深入)”;
- TP6:依赖模型
$json+$jsonType,不声明就无自动处理 - TP8:默认对
json类型字段启用自动 decode(目标类型为array),但仅限于通过find/select查询出的字段;getData()或__get()访问仍可能返回字符串,取决于底层驱动是否注入了 typecast - TP8 中若字段在数据库里是
text但内容是 JSON,TP 不会自动处理——必须靠访问器或手动 decode - 跨版本迁移时,别假设
$json声明在 TP8 里还起同样作用;建议统一用访问器getExtraAttr控制行为,更可控
JSON 字段真正难的不是存和取,而是混合使用原生 SQL 查询、whereJsonContains、聚合函数时,类型隐式转换容易失效。比如 where('extra->name', 'like', '%a%') 在 MySQL 里走的是 JSON 路径表达式,TP 不会帮你 decode,这时候字段值类型就和模型层完全脱钩了。


















