ThinkPHP 6.0 操作 MySQL JSON 字段需显式启用 JSON 支持:写入须用 json() 方法或模型 $json 属性,否则存为序列化字符串;读取需声明 json() 才自动解码;查询须用 字段名->键名 路径语法并配合 setFieldType 避免类型误判;修改支持局部更新(如 profile->hobby)和删除键(赋值为 null)。

在 ThinkPHP 6.0 中直接对 MySQL 的 JSON 字段执行增删改查操作时,若未显式启用 JSON 支持,写入会变成字符串、查询返回原始 JSON 文本、条件查询完全失效——最终数据存得进去却取不出来,或查到的永远是空数组。
写入 JSON 字段
方法一:使用 Db 查询器 + json() 方法声明字段类型
Db::table('user')->json(['profile', 'settings'])->insert(['name' => '李四', 'profile' => ['age' => 28, 'city' => '杭州'], 'settings' => ['theme' => 'dark', 'notify' => true]]);
这一步必须显式调用 json(['字段名1', '字段名2']),否则 TP 会把数组序列化成类似 s:4:"Array"; 的 PHP 序列化字符串,MySQL 直接报错“Invalid JSON text”。
方法二:模型层自动处理(推荐)
在模型类中定义 protected $json = ['profile', 'settings'];
然后直接赋值保存:
$user = new UserModel();
$user->name = '王五';
$user->profile = ['age' => 32, 'job' => '前端'];
$user->settings = ['lang' => 'zh-CN'];
$user->save();
TP 会自动对 $json 数组中的字段调用 json_encode,且默认启用 JSON_UNESCAPED_UNICODE,中文不会乱码。
读取 JSON 字段内容
Db 查询器方式需同步声明 json():
Db::table('user')->json(['profile'])->where('id', 1)->find();
不加 json(['profile']) 就只能拿到原始 JSON 字符串,比如 '{"age":28,"city":"杭州"}',不是 PHP 数组。
立即学习“PHP免费学习笔记(深入)”;
模型方式更省心:
$user = UserModel::find(1);
echo $user->profile['age']; // 直接当数组用
但前提是模型已声明 protected $json = ['profile']; 否则 $user->profile 返回的是字符串,强行下标访问会报错。
查询 JSON 内部键值
第一步:用 JSON 路径语法写 where 条件
Db::table('user')->json(['profile'])->where('profile->age', 28)->select();
注意路径写法必须是 字段名->键名,不能写成 profile.age 或 profile['age']。
将一个或多个视频上传至YouTube。当用户想“上传到YouTube”“发YouTube”“批量上传”“upload to YouTube”“post videos to YouTube”或发布到YouTube时使用此技能。
第二步:数值类型需配合 setFieldType 显式声明
Db::table('user')->json(['profile'])->where('profile->age', 28)->setFieldType(['profile->age' => 'int'])->select();
否则 MySQL 返回的 age 值会被当字符串比较,28 ≠ '28' 导致查不到。
第三步:模糊匹配或数组包含用原生函数
Db::table('user')->whereRaw('JSON_CONTAINS(profile, ?, "$.tags")', [['php', 'vue']])->select();
这里用 ? 占位符绑定参数,避免 SQL 注入;路径 "$.tags" 必须用双引号包裹,单引号会失效。
修改 JSON 字段局部内容
方法一:整体替换
$data['profile'] = ['age' => 29, 'city' => '上海', 'hobby' => '骑行'];
Db::table('user')->json(['profile'])->where('id', 1)->update($data);
整个 profile 字段被新数组完全覆盖,旧值丢失。
方法二:只改某个键(TP 6.0 原生支持)
$data['profile->hobby'] = '登山';
Db::table('user')->json(['profile'])->where('id', 1)->update($data);
这条语句等价于 MySQL 的 JSON_SET(profile, '$.hobby', '登山'),其他键不受影响。
方法三:删除某个键
$data['profile->city'] = null;
Db::table('user')->json(['profile'])->where('id', 1)->update($data);
TP 会自动转为 JSON_REMOVE(profile, '$.city'),前提是该 JSON 字段建表时允许 NULL。
删除整条含 JSON 的记录
和普通删除无区别:
Db::table('user')->where('id', 1)->delete();
或模型方式:
UserModel::destroy(1);
JSON 字段本身不参与删除逻辑,删的是整行数据。


















