Laravel多对多查询:如何筛选不包含全部指定标签的文章
问题分析与解决方案
你的代码逻辑完全不符合需求:whereDoesntHave('tags', ...)会直接排除所有关联了1、2、3中任意一个标签的帖子,这显然不是你想要的效果——你需要的是只排除标签恰好等于[1,2,3]的帖子,而保留以下几类帖子:
- 只包含1、2、3中的部分标签(比如[1,2]、[2,3])
- 包含1、2、3全部标签且有额外标签(比如[1,2,3,4])
- 完全不包含1、2、3中任何标签的帖子
正确代码实现
$tagIds = [1,2,3]; $tagCount = count($tagIds); $posts = Post::whereNot(function ($query) use ($tagIds, $tagCount) { // 筛选出:标签数量恰好等于指定标签数,且所有标签都在指定列表中的帖子 $query->whereHas('tags', function ($q) use ($tagCount) { $q->groupBy('post_id') ->havingRaw('count(*) = ?', [$tagCount]); })->whereHas('tags', function ($q) use ($tagIds, $tagCount) { $q->whereIn('id', $tagIds) ->groupBy('post_id') ->havingRaw('count(*) = ?', [$tagCount]); }); })->get();
代码逻辑说明
- 外层
whereNot用来排除我们不需要的帖子 - 第一个
whereHas:筛选出标签总数恰好为3的帖子 - 第二个
whereHas:在上述帖子中,进一步筛选出所有标签都属于[1,2,3]的帖子 - 两者结合,就精准排除了标签恰好是[1,2,3]的帖子,其余所有帖子都会被保留
如果你的真实需求是排除所有包含全部指定标签的帖子(不管有没有额外标签),比如[1,2,3]和[1,2,3,4]都要排除,那代码可以简化为:
$tagIds = [1,2,3]; $tagCount = count($tagIds); $posts = Post::whereDoesntHave('tags', function ($query) use ($tagIds, $tagCount) { $query->whereIn('id', $tagIds) ->groupBy('post_id') ->havingRaw('count(*) = ?', [$tagCount]); })->get();
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

