必须用wherehas与orwheredoesnthave组合or逻辑,外层where包裹闭包,内部分别匹配有已发布文章或无任何文章的用户,注意软删除处理及多关联表or判断需统一闭包收口。

要筛选出“有已发布文章的用户”或“完全没发过文章的用户”,不能只用whereHas或whereDoesntHave单独写,必须把两者逻辑组合进同一个查询条件里,否则会漏掉其中一类数据。
用whereHas与whereDoesntHave组合OR关系
第一步:用外层where()包裹整个OR逻辑,避免AND优先级干扰原有筛选条件。
第二步:在闭包内先写whereHas('posts', fn($q) => $q->where('status', 'published')),匹配有已发布文章的用户。
第三步:接着写orWhereDoesntHave('posts'),表示“或者没有posts关联记录”。注意这里不能写成whereDoesntHave(),否则会被当成AND条件;【必须用orWhereDoesntHave()才能实现OR语义】。
第四步:补全其他主表筛选,比如->where('active', 1),再调用->get()执行。
同时排除软删除关联记录的陷阱
默认情况下,whereDoesntHave()不会把已软删除的posts算作“存在”,所以它天然跳过软删除记录——这点和whereHas不同。
但如果你的业务要求把软删除的posts也视为“存在”,就得改写whereDoesntHave为whereDoesntHave('posts', fn($q) => $q->withoutTrashed()),否则逻辑会错位。
这一步容易被忽略:不加withoutTrashed()时,软删除的posts对whereDoesntHave无影响;加了反而会让它去查那些已被标记删除的记录,结果变成“既不算存在也不算不存在”的中间态。
多个关联表联合OR判断(如posts或comments)
方法一:嵌套两层orWhereHas + orWhereDoesntHave
直接写whereHas('posts') → orWhereHas('comments') → orWhereDoesntHave('posts') → orWhereDoesntHave('comments')会导致逻辑混乱,SQL生成错误。
方法二:用where(fn($q) => ...)统一收口
把所有OR分支放进一个闭包里,用$q->whereHas()、$q->orWhereHas()、$q->orWhereDoesntHave()平级排列,Eloquent才能正确转成WHERE (A OR B OR C)结构。
方法三:避免跨表字段混用
不要在同一个orWhereDoesntHave闭包里试图查posts.status,因为whereDoesntHave只关心“是否存在”,不支持字段条件——要筛状态就得回到whereHas里做。











