thinkphp中$json属性必须显式声明,否则json字段读取为字符串而非数组;需同时配置$protected $json和$protected $type才能实现双向编解码;修改数组内容须整体赋值,且避免与$withattr混用导致重复解析。

$json 属性必须显式声明,否则 JSON 字段读出来永远是字符串,不是数组——这不是 bug,是 ThinkPHP 的默认行为。
为什么 $json 不写就无效?
ThinkPHP 不会根据数据库字段类型(如 MySQL 的 JSON)自动启用 JSON 解析。哪怕你用的是 MySQL 5.7+,只要模型里没写 protected $json = ['extra'];,$model->extra 拿到的就是原始字符串,不是数组。
常见错误现象:
-
foreach($model->extra as $k => $v)报错Fatal error: Cannot use object as array -
var_dump($model->extra)输出string(24) "{"name":"a","age":25}",而不是array(2) {["name"]=> string(1) "a" ...}
验证方式:在出错前加一句 var_dump(gettype($model->extra), $model->extra);,如果第一个是 string,说明 $json 没配对或字段名拼错(大小写敏感)。
$json 和 $type 要一起用才完整
只写 $json 能让读取时自动 json_decode,但写入时不会自动 json_encode——除非你还配了 $type:
-
protected $type = ['extra' => 'json'];:声明字段类型为 JSON,配合$json启用双向编解码 -
protected $json = ['extra'];:告诉模型“这个字段要走 JSON 流程” - 缺一不可:只写
$type不生效;只写$json,写入数组会报Array to string conversion
注意:$type['extra'] = 'json' 要求数据库字段是 TEXT 或原生 JSON 类型;如果是逗号分隔字符串,不能硬套 'json',得用自定义访问器。
修改数组内容后必须整体赋值才能保存
ThinkPHP 不监听数组内部变化。以下写法无效:
$model->extra['age'] = 28; $model->save(); // extra 字段不会更新
正确做法是先改完再整体赋值:
$data = $model->extra; $data['age'] = 28; $model->extra = $data; $model->save();- 或者一步到位:
$model->extra = array_merge($model->extra, ['age' => 28]); $model->save();
别依赖“链式修改”,TP 不做 dirty tracking,只检测字段级是否被重新赋值。
避免和 $withAttr 冲突导致重复 decode
如果你同时写了:
protected $json = ['setting']; protected $withAttr = ['setting' => 'json_decode'];
结果是:TP 先按 $json 解一次,再进 $withAttr 又对数组调一次 json_decode,返回 null,数据就丢了。
建议原则:
- 标准 JSON 字段,只用
$json,轻量且稳定 - 需要 fallback 默认值、兼容旧格式(比如空字符串转
[])、或字段含点号(user.profile)等场景,才用getSettingAttr自定义访问器 - 不要混用
$json和$withAttr处理同一个字段
真正容易被忽略的点是:字段名大小写、数据库实际存储是否合法 JSON(null 或空字符串不会自动转成空数组)、以及用 Db::table() 直接查时 $json 完全不生效——必须走模型实例。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











