ThinkPHP 6.1+ 废弃 hasMany 等链式调用,必须用闭包定义关联条件;因 HasMany 实例已移除 where 等方法,直接调用会报错或失效,需在关联方法内返回带条件的 Query 对象。

ThinkPHP 6.1+ 中 hasMany、belongsTo 等链式调用写法已废弃,必须改用闭包定义关联,否则运行时直接报错或关联失效。
为什么旧写法会出错:hasMany 返回对象不再支持链式调用
旧版(TP5.x)里常这么写:$user->posts()->where('status', 1)->select();TP6.1+ 的关联方法(如 hasMany)返回的是 think\model\relation\HasMany 实例,但它的 where、order 等方法已被移除——这些操作现在必须在定义关联时用闭包预设,或通过 with(['posts' => function ($q) { $q->where('status', 1); }]) 动态传入。
- 直接调用
->where()会触发Call to undefined method think\model\relation\HasMany::where() - 即使没报错,
->select()也可能返回空结果,因为关联查询逻辑已重构 - 模型中用
protected $with = ['posts']时,若posts关联未用闭包定义条件,就无法复用带筛选的关联逻辑
新写法怎么定义带条件的关联:在模型里用闭包返回 Query
关联方法体内必须返回一个可被 with 或 load 消费的 Query 对象,典型模式是 return $this->hasMany(Post::class, 'user_id')->where('status', 1) —— 注意 where 是作用在关联查询的 Query 上,不是关系实例上。
- 正确示例:
public function posts() { return $this->hasMany(Post::class, 'user_id')->where('status', 1); } - 若需动态参数(比如按传入状态查),不能在闭包外捕获变量,得用
with显式传:$user->with(['posts' => function ($q) use ($status) { $q->where('status', $status); }]) - 关联定义里避免调用
select()或find(),那会导致提前执行,破坏懒加载和批量查询优化
关联预载时加条件:别在 with 外层拼 SQL,要用闭包参数
with 方法第二个参数接受闭包,这才是控制预载查询条件的唯一安全入口。写成 $user->with('posts')->where(...) 是无效的——那个 where 作用于主模型,不是关联表。
立即学习“PHP免费学习笔记(深入)”;
- 错误:
$user->with('posts')->where('posts.status', 1)->select()→ 报错或忽略 - 正确:
$user->with(['posts' => function ($query) { $query->where('status', 1); }])->select() - 多个关联同时加条件:
with(['posts' => ..., 'profile' => function ($q) { $q->field(['id', 'avatar']); }]) - 注意:闭包里的
$query是think\db\Query,不是模型实例,不支持模型事件或自动时间戳
容易被忽略的兼容细节:软删除、时间字段、自定义主键
新关联定义下,软删除字段(如 delete_time)不会自动过滤,除非你在闭包里显式加上 ->whereNull('delete_time');同理,自定义外键名(如 author_id)必须在 hasMany 第二个参数明确传入,不能依赖约定。
- 软删除需手动处理:
return $this->hasMany(Comment::class, 'post_id')->whereNull('delete_time') - 自定义外键别漏参数:
$this->hasMany(Reply::class, 'comment_id'),而不是默认的id - 时间范围查询(如「最近7天评论」)必须写在闭包内:
->where('create_time', '>=', time() - 86400 * 7) - 关联模型若也用了软删除,父模型的
with不会自动穿透过滤,得在子关联定义里各自处理
闭包定义看着多写几行,其实是把「什么时候查、查什么条件」的控制权交还给开发者——TP6+ 的 ORM 更强调显式优于隐式,所有条件都得落在 query 层,模型层只负责组织关系结构。漏掉一个 whereNull 或错用外键参数,关联数据就可能对不上,这种问题在线上不容易被日志捕获,得靠单元测试覆盖关键路径。


















