laravel多层筛选需用wherehas嵌套关联条件避免n+1问题:先wherehas('orders')内设时间与金额条件;多级关联可用连续wherehas或dot语法(9.28+);子查询复用字段须用db::raw+addselect+havingraw。

在Laravel中实现多层数据筛选时,需用嵌套查询避免N+1问题并保持链式调用的可读性,比如先查出活跃用户,再从中筛选近30天有订单且订单总金额超500元的用户,不能靠简单with()加载后PHP过滤。
用whereHas嵌套关联条件
第一步:在User模型查询中调用whereHas,传入关联关系名和闭包。
第二步:闭包内对Order模型写条件,如where('created_at', '>=', now()->subDays(30))→where('total_amount', '>', 500)。
这一步必须写在whereHas闭包里,若把where('total_amount', '>', 500)提到外层,会查出所有用户再过滤,失去嵌套意义。
多级关联嵌套(用户→订单→订单项→商品)
方法一:连续嵌套whereHas
whereHas('orders', function ($q) { $q->whereHas('items', function ($q2) { $q2->whereHas('product', function ($q3) { $q3->where('category_id', 1); }); }); });
方法二:用dot语法简化(仅适用于Laravel 9.28+)
whereDoesntHave('orders.items.product', function ($q) { $q->where('status', 'archived'); });
【注意:dot语法不支持在闭包内混用select或join,否则报错】
在子查询中复用主查询字段
第一步:用DB::raw()构造子查询别名,例如(select count(*) from orders where orders.user_id = users.id) as order_count。
第二步:用addSelect()将该子查询加入主查询字段列表。
第三步:用havingRaw()对子查询结果做筛选,如havingRaw('order_count > 3')。
这一步不能用whereRaw,因为order_count是SELECT阶段生成的别名,WHERE无法识别,必须用HAVING。











