必须使用 MySQL 5.7+ 且字段类型为 JSON,ThinkPHP 的 json() 方法、-> 查询及 $json 配置才有效;否则静默退化为字符串处理,导致查询失败或数据异常。

必须用 MySQL 5.7+ 且字段类型为 JSON,否则 ThinkPHP 的 json() 方法、-> 查询、$json 模型配置全部失效——不是报错,而是静默退化成字符串处理。
确认 MySQL 版本和字段类型是第一件事
ThinkPHP 对 JSON 的所有高级能力都建立在 MySQL 原生函数之上,没这层底座,上层语法全是空转。
- 执行
SELECT VERSION();,结果必须 ≥5.7.0(推荐 8.0+) - 执行
SHOW COLUMNS FROM user LIKE 'info';,Type列必须显示json,不是text、varchar或longtext - 字段是
TEXT时硬套where('info->email', 'a@b.com'),MySQL 直接报错FUNCTION JSON_EXTRACT does not exist或返回空结果
json(['field']) 必须显式调用,否则字段当普通字符串处理
哪怕字段类型是对的,不告诉 ThinkPHP “这个字段要走 JSON 流程”,它就永远不会触发 json_encode/json_decode,也不会生成 JSON_EXTRACT 查询。
- 写入时漏掉
json(['info']):传数组进去,入库变成Array字符串或序列化内容,后续查不到 - 查询时漏掉:返回的
$user['info']是原始 JSON 字符串,不是 PHP 数组;where('info->email', ...)会生成非法 SQL - Db 类操作必须每次带上:
Db::name('user')->json(['info'])->insert([...]),不能只在模型里配$json就以为全局生效
模型里配 $json 和 $type 才能自动解码
仅靠数据库字段类型和 json() 方法,只能保写入和基础查询;想让 find() 返回直接可用的数组,模型层必须双配置。
立即学习“PHP免费学习笔记(深入)”;
- 必须同时定义:
protected $json = ['info'];+protected $type = ['info' => 'json'];,缺一不可 - 字段名必须完全一致(含大小写、下划线),
info和user_info是两个字段 -
$type写成'array'也能工作,但语义模糊,且不兼容 MySQL 原生 JSON 函数,推荐统一用'json' - 如果数据库该字段值是
NULL,$user->info就是null,不是空数组,需手动判断:is_array($user->info) ? $user->info : []
-> 和 ->> 的区别决定你能不能查到数据
这是最隐蔽也最容易踩的坑:MySQL 的 -> 返回带引号的 JSON 字符串,->> 才返回去引号的纯值。ThinkPHP 不封装 ->>,必须手动写。
- 错误写法:
where('info->status', 'active')→ 实际比的是"active"==active,永远不成立 - 正确写法:
where('info->>status', 'active')→ 等价于JSON_UNQUOTE(JSON_EXTRACT(info, "$.status")) = "active" - 数字比较同理:
where('info->>score', '>', 90)安全;where('info->score', '>', 90)会把90当字符串比,"95">"90"成立,但"100"可能被截断或误判 - 路径中的键名不能拼接用户输入,
'info->>'.input('key')是严重注入点,必须白名单校验或硬编码
真正麻烦的从来不是“怎么写”,而是字段类型、MySQL 版本、TP 配置、JSON 路径语法这四层是否严丝合缝对齐——漏掉任何一层,表现都是“查不到”或“返回 null”,但原因各不相同。



















