
使用 Laravel Eloquent 的 whereDoesntHave 与 whereHas 组合,可高效筛选出“所有关联子记录均满足某条件”的父模型,避免手动聚合或子查询,兼顾可读性与性能。
使用 laravel eloquent 的 `wheredoesnthave` 与 `wherehas` 组合,可高效筛选出“所有关联子记录均满足某条件”的父模型,避免手动聚合或子查询,兼顾可读性与性能。
在实际业务中,常需查找“所有子项均符合特定状态”的父级数据(例如:订单下所有商品均已发货、项目下所有任务均已完成)。本例中,目标是获取 所有子记录 status = 3 且至少存在一个子记录 的父级(Parent)模型。
Laravel 提供了语义清晰的关联约束方法:
- whereHas('children') 确保父记录存在至少一个子记录(排除无子项的孤儿父级);
- whereDoesntHave('children', fn($q) => $q->where('status', '!=', 3)) 则表示“不存在任何 status ≠ 3 的子记录”,即所有子记录的 status 都必须等于 3。
完整代码如下:
$parents = Parent::whereDoesntHave('children', function ($query) {
$query->where('status', '!=', 3);
})
->whereHas('children')
->get();
✅ 执行逻辑等价于以下 SQL(由 Eloquent 自动生成):
SELECT * FROM `parents`
WHERE NOT EXISTS (
SELECT * FROM `children`
WHERE `parents`.`id` = `children`.`parent_id` AND `status` != 3
)
AND EXISTS (
SELECT * FROM `children`
WHERE `parents`.`id` = `children`.`parent_id`
);
⚠️ 注意事项:
- 关联关系必须正确定义:Parent 模型中需声明 public function children() { return $this->hasMany(Child::class); };
- 若允许子表为空(即 status 可为 NULL),需额外处理:whereNull('status') 或 whereNotNull('status'),避免 NULL != 3 判定失效;
- 性能方面,建议在 children.parent_id 和 children.status 字段上建立联合索引(如 INDEX parent_id_status (parent_id, status)),显著提升 EXISTS/NOT EXISTS 子查询效率。
该方案简洁、可读性强,无需手写原生 SQL 或复杂 GROUP BY + HAVING 聚合,是 Laravel 中处理“全量匹配”类查询的标准实践。











