You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 20:50:04