ThinkPHP 6 的 json() 默认不转换字段名,需手动用 Str::camel 转换键名或自定义 Response 类统一处理;反向需在 input() 中用 Str::snake 解析请求体,验证器仍用下划线。

ThinkPHP 6 的 json 输出默认不转换字段名
默认情况下,return json($data) 会原样输出数组键名,不会把 user_name 变成 userName,也不会反向处理。这不是 Bug,是设计如此——框架不干涉数据结构,由开发者显式控制。
常见错误现象:axios.get('/api/user').then(res => console.log(res.data.user_name)) 报错,因为前端期望的是 userName,但后端返回了 user_name。
- 只在 API 场景需要映射;模板渲染、写入数据库等场景不该介入
-
json()是快捷封装,底层仍是Response对象,可干预序列化前的数据 - 不要改全局
think\facade\Config的app.json_encode,它影响所有 JSON 输出(包括错误页),容易误伤
用 think\helper\Str::camel 手动转换数组键
最轻量、最可控的方式:在返回前遍历并重命名键名。适用于单次响应或少量接口。
示例:
立即学习“PHP免费学习笔记(深入)”;
$data = ['user_id' => 123, 'last_login_time' => '2024-05-01'];
$converted = array_map(function ($item) {
if (is_array($item)) {
return array_combine(
array_map([think\helper\Str::class, 'camel'], array_keys($item)),
array_values($item)
);
}
return $item;
}, $data);
return json($converted);
-
think\helper\Str::camel支持带数字的下划线(如order_no_2→orderNo2),比正则更稳 - 不能递归处理嵌套数组,需自己加
is_array()判断,否则报array_keys() expects parameter 1 to be array - 性能影响极小,但别在高频接口里反复调用
array_map + array_combine,可封装成复用函数
通过 think\Response 自定义 JSON 序列化逻辑
适合中大型项目,统一处理所有 API 响应。核心是替换默认的 toJson 行为,而不是改 json_encode 配置。
做法:在 app/common/Response.php 中继承 think\Response,重写 output 方法:
protected function output($data): string
{
if (is_array($data) && request()->isAjax()) {
$data = $this->convertKeysToCamel($data);
}
return parent::output($data);
}
- 必须加
request()->isAjax()或检查Accept: application/json,否则 HTML 模板也会被转换,导致模板变量找不到(如$user_name变成$userName) -
convertKeysToCamel要支持多层嵌套,建议用递归 +think\helper\Str::camel,别用json_encode + preg_replace,后者无法保证键值对应 - 注册该类需在
app/middleware/Response.php或控制器基类中手动 new,不走容器自动绑定时,return response()->json(...)不会生效
反向兼容:前端传 userName,后端要能识别为 user_name
POST/PUT 请求体里的字段也要映射,否则 $this->request->post('userName') 拿不到值。ThinkPHP 不提供自动反向转换,得自己接住原始输入。
推荐在基控制器里统一处理:
protected function input(): array
{
$raw = $this->request->param();
return $this->convertKeysToSnake($raw);
}
-
convertKeysToSnake是camel的逆操作,可用think\helper\Str::snake,注意它默认转为小写下划线,符合 Laravel 风格 - 别覆盖
$this->request->post(),它内部有类型转换和过滤逻辑,直接改原始param()更安全 - 如果用了验证器(
Validate),字段名仍需保持下划线,否则规则失效(如'user_name.require' => '用户名必填')
字段映射这事没有银弹。驼峰和下划线混用时,最容易被忽略的是「请求体解析」和「验证器字段名」的不一致,这两处一旦漏掉,前端看着正常,后端查不到参数或验证总失败。


















