Laravel中如何从关联表筛选数据?附订单关联表结构与模型
嘿,我来帮你搞定Laravel里这几张关联表的数据筛选问题~首先先确认下,你的OrderStatus模型应该还需要和Status模型建立关联吧?先补上这个关联会让筛选更顺畅:
在OrderStatus模型里添加:
public function status() { return $this->belongsTo(Status::class); }
接下来分几种常见的筛选场景给你举例:
1. 筛选曾经出现过特定状态的订单
比如你要找所有历史状态里包含「已发货」(假设status表中该状态id为2)的订单,用whereHas来检查关联记录是否满足条件:
$targetOrders = Order::whereHas('orderStatus.status', function ($query) { // 这里可以根据status表的字段筛选,比如id或者状态名称 $query->where('id', 2); // 或者 $query->where('name', '已发货'); })->get();
whereHas的作用是:只保留那些存在符合条件的关联记录的主模型(这里就是Order),嵌套orderStatus.status直接关联到状态表的字段,不用自己写join语句。
2. 获取订单,同时只加载符合特定状态的关联记录
如果你想拿到订单数据,但每个订单只需要加载它的某类状态记录(比如所有「退款中」的状态事件),用with配合闭包筛选关联:
$ordersWithRefundStatus = Order::with(['orderStatus' => function ($query) { $query->where('status_id', 4) // 假设退款中对应的status_id是4 ->with('status'); // 同时加载对应的状态详情 }])->get();
这样返回的每个Order模型里,orderStatus属性只会包含符合条件的状态记录,不会把所有历史状态都带回来。
3. 筛选最新状态符合特定条件的订单
因为order_status是跟踪订单的事件记录,最新的状态一般是最后创建的那条。如果你要找当前最新状态是「已完成」的订单,可以这么写:
$completedOrders = Order::whereHas('orderStatus', function ($query) { $query->orderBy('created_at', 'desc') // 按创建时间倒序,取最新的一条 ->limit(1) ->whereHas('status', function ($statusQuery) { $statusQuery->where('name', '已完成'); }); })->get();
这里通过orderBy+limit(1)锁定最新的状态记录,再检查这条记录的状态是否符合要求。
4. 多条件组合筛选
比如要找曾经支付过,但现在状态是已取消的订单,就可以组合多个whereHas:
$targetOrders = Order::whereHas('orderStatus.status', function ($query) { // 曾经有过支付状态 $query->where('name', '已支付'); })->whereHas('orderStatus', function ($query) { // 最新状态是已取消 $query->orderBy('created_at', 'desc') ->limit(1) ->whereHas('status', function ($statusQuery) { $statusQuery->where('name', '已取消'); }); })->get();
最后提个小注意:如果你的表名或者外键不是Laravel默认的命名规则(比如order_status的外键不是order_id/status_id),记得在关联方法里指定外键参数,比如$this->belongsTo(Status::class, 'status_id');(不过看你的表结构,默认规则是匹配的,应该没问题~)
内容的提问来源于stack exchange,提问作者user7747472

