Eloquent多条件关联查询问题:筛选特定状态的预约与收据
问题分析
你遇到的问题本质是:whereHas 仅用于筛选存在符合条件关联数据的主模型(Reservation),但不会影响 with 方法加载关联数据的范围——所以即使预约存在未付款收据,with('bill.receipts') 仍然会加载该预约下的所有收据(包括已付款的)。
解决方案
要实现「筛选状态为Checked-In的预约,且仅加载其关联的UNPAID状态收据」,需要在用whereHas筛选主模型的同时,给with的关联加载添加条件约束,确保只返回符合要求的收据。
正确写法示例
$reservations = Reservation::with([ 'rooms', 'bill.receipts' => function ($query) { // 给receipts关联添加过滤条件,只加载UNPAID状态的 $query->where('status', 'UNPAID'); }, 'bill.receipts.foodOrders.food' ]) ->where('status', 'Checked-In') ->whereHas('bill.receipts', function ($query) { // 确保预约存在至少一个UNPAID状态的收据 $query->where('status', 'UNPAID'); }) ->get();
写法说明
whereHas('bill.receipts', ...):负责过滤出存在未付款收据且状态为Checked-In的预约,这一步保证不会返回完全没有未付款收据的预约。with('bill.receipts' => function ($query) {...}):在加载receipts关联时,额外添加status = 'UNPAID'的条件,确保最终返回的每个预约下只包含未付款的收据。- 关联嵌套(
bill.receipts.foodOrders.food)会自动继承receipts的过滤条件,只加载未付款收据对应的食品订单和食品信息。
另一种等效写法(拆分whereHas层级)
如果你习惯拆分关联层级,也可以写成:
$reservations = Reservation::with([ 'rooms', 'bill.receipts' => fn($query) => $query->where('status', 'UNPAID'), 'bill.receipts.foodOrders.food' ]) ->where('status', 'Checked-In') ->whereHas('bill', function ($query) { $query->whereHas('receipts', fn($subquery) => $subquery->where('status', 'UNPAID')); }) ->get();
为什么从Receipt查询能正常工作
因为从Receipt出发时,你直接对Receipt模型添加了where('status', 'UNPAID')条件,同时通过with加载关联的预约,自然只会返回未付款的收据及其关联数据——这和从Reservation出发的逻辑方向相反,但核心都是对需要返回的关联数据添加直接过滤。
内容的提问来源于stack exchange,提问作者Xiaofan
相关产品推荐
相关产品推荐

