yii2更新json字段必须手动json_encode(),因ar不自动序列化;需用json_unescaped_unicode和json_unescaped_slashes处理中文与斜杠,读取时也须手动json_decode()并校验错误。

Yii2 更新 JSON 字段时必须用 json_encode() 手动序列化
Yii2 的 ActiveRecord 默认不会自动把 PHP 数组转成 JSON 存入数据库,即使字段类型是 JSON(MySQL 5.7+)或 TEXT。直接赋值数组会存成 Array 字符串或触发错误。
正确做法是显式调用 json_encode(),并确保编码无错(如处理中文、空值、循环引用):
$model = MyModel::findOne(123);
$model->config = json_encode([
'theme' => 'dark',
'notifications' => ['email' => true, 'sms' => false],
'lang' => 'zh-CN'
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$model->save();
- 必须加
JSON_UNESCAPED_UNICODE,否则中文变\uXXXX - 不加
JSON_UNESCAPED_SLASHES时,URL 类内容(如"url": "https://a.com")会被转义成"https:\/\/a.com" - 如果数组含不可序列化对象(如 Closure、Resource),
json_encode()返回false,需提前过滤或抛异常
用 ActiveRecord::attributes() 批量更新 JSON 字段要格外小心
批量赋值(如 $model->load($post) 或 $model->setAttributes($data))时,若 $data['config'] 是数组,它不会自动 encode —— 仍会以数组形式塞进属性,save() 时可能报 PDO 错误或存空字符串。
安全写法是先清理输入,再手动 encode:
$postData = Yii::$app->request->post();
$config = $postData['MyModel']['config'] ?? [];
// 确保是数组,且不含非法值
if (!is_array($config)) {
$config = [];
}
$model = new MyModel();
$model->setAttributes(array_merge($postData['MyModel'], [
'config' => json_encode($config, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
]));
$model->save();
- 别依赖
rules()中的'config' => 'safe'来“放行”数组 —— 安全性 ≠ 自动序列化 - 如果前端传的是已 encode 的 JSON 字符串(如
{"theme":"dark"}),后端需先json_decode($raw, true)再 encode,避免双重编码
MySQL JSON 字段用 JSON_SET() 做局部更新更高效
如果只改 JSON 里的某个键(比如只更新 config.notifications.email),用 PHP 全量 decode → 修改 → encode → save 会读写整列,还可能覆盖并发修改。直接走 SQL 的 JSON_SET() 更轻量:
Yii::$app->db->createCommand("UPDATE {{%my_model}}
SET [[config]] = JSON_SET([[config]], '$.notifications.email', :email)
WHERE [[id]] = :id")
->bindValue(':email', true)
->bindValue(':id', 123)
->execute();
-
JSON_SET()要求 MySQL ≥ 5.7.8;低版本只能用字符串函数(不推荐) - 路径语法用
$.key.nested,注意单引号包裹整个 JSON 路径字符串 - 该方式绕过 ActiveRecord 生命周期(验证、事件、脏检查),适合后台脚本或高频局部更新场景
读取时记得用 json_decode() 解包,别假设自动转换
Yii2 不会自动把数据库里存的 JSON 字符串反序列化成数组 —— 从 $model->config 拿到的永远是原始字符串(哪怕字段定义为 'config' => 'json' 在 migration 里)。必须手动解码:
$config = json_decode($model->config, true);
if (json_last_error() !== JSON_ERROR_NONE) {
$config = []; // 解析失败兜底
}
$theme = $config['theme'] ?? 'light';
- 生产环境务必检查
json_last_error(),空字符串、null、格式错误都可能导致null返回 - 不要在
getters里无条件json_decode—— 如果字段为空或非法,每次访问属性都会触发解析开销和错误判断 - 可封装成一个安全 getter:
public function getConfig(): array { return json_decode($this->config, true) ?: []; }
json_encode() 失败的情况 —— 这会导致数据库存入 NULL 或空字符串,但日志里没有任何报错。











