ThinkPHP 6 的 input() 默认不支持多层嵌套 JSON 点号路径访问,需手动 json_decode 或 bind 扁平化数据;validate() 和 allowField() 同样不识别嵌套键名,须先 array_dot() 转换;混合提交需按 Content-Type 分别解析并合并。

ThinkPHP 6 的 input() 怎么处理多层嵌套 JSON 数据
默认情况下,input() 只解一层嵌套,比如 {"user":{"name":"a","profile":{"age":25}}} 里 input('user.profile.age') 会返回 null —— 不是 bug,是设计如此,它不递归解析点号路径。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
input('', '', false)关闭自动过滤,再手动json_decode(input(''), true),这是最可控的方式 - 若坚持用点号访问,需提前在控制器里调用
think\facade\Request::bind($data)手动绑定扁平化后的数组(比如用array_dot()处理) - 注意 Content-Type 必须是
application/json,否则input()不触发 JSON 自动解析
如何让 validate() 正确校验嵌套字段(如 user.email、address.city)
验证器默认只认一级键名,user.email 这种写法在规则数组里直接写会失效,因为验证器没做路径展开。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 规则中仍写一级字段名,但传入数据前先用
array_dot()扁平化:$flat = \think\Helper::arrayDot($data),再交给validate()->check($flat) - 自定义验证场景时,可在
scene()方法里用only(['user_email', 'address_city'])配合字段映射,避免动态拼接规则 - 别在验证规则里写
'user.email'=> 'require|email',这会被当成字面键名查找,实际数据里并不存在这个 key
model->allowField() 对嵌套数组赋值失效?
allowField() 只过滤一维键名,遇到 ['user'=>['name'=>'x'],'meta'=>['tag'=>'y']] 这类结构,直接 $model->allowField(['user.name','meta.tag'])->save($data) 不起作用 —— 它根本不会拆解点号。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 改用
data()+isUpdate()分步操作:先$model->data($flatData)->isUpdate(false)->save(),确保传入的是纯一维数组 - 在模型的
save()前加钩子,用array_walk_recursive()提取需要的字段,或用array_filter()配合Str::contains()做白名单匹配 - 如果字段固定,直接在模型里定义
protected $type = ['user_name'=>'string', 'meta_tag'=>'json'],配合自动转换更稳妥
兼容历史接口:POST 表单含 user[name] 和 JSON 混合提交
前端可能同时发 application/x-www-form-urlencoded(带方括号嵌套)和 application/json,TP6 默认只按一种方式解析,容易丢数据。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 统一入口先判断
Request::contentType(),如果是表单类型,用Request::param();JSON 类型用Request::post();再手动合并 - 禁用自动解析:在中间件里设
$request->filter([]),避免htmlspecialchars破坏嵌套结构 - 方括号语法(如
user[profile][avatar])可用parse_str()+ 自定义递归转义函数还原,但注意 PHP 版本对深度限制(默认 512 层)
嵌套层级越深,array_dot() 和 array_undot() 的 key 名长度越不可控,尤其当字段名本身含点号或中括号时,得提前清洗。别指望框架自动猜你想要哪一层。



















