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

Laravel Eloquent查询优化:需包含带练习的标题(即使同时关联测验/竞赛),仅排除无练习的测验/竞赛类标题

Laravel Eloquent查询优化:需包含带练习的标题(即使同时关联测验/竞赛),仅排除无练习的测验/竞赛类标题

我完全get到你的问题了——你现在的查询错误地把所有带测验或竞赛的标题都排除了,但你真正想要的是:只要标题关联了练习,不管它有没有测验/竞赛,都要保留在结果里,而且返回的时候只展示练习部分;只有那些完全没练习、只有测验/竞赛的标题才需要排除。咱们来修正这个逻辑:

问题根源分析

你原来的代码里用了whereDoesntHave('quizzes')和whereDoesntHave('contests'),这两个条件的意思是「这个标题完全没有测验」和「这个标题完全没有竞赛」,所以只要标题沾了测验/竞赛的边(哪怕同时有练习),都会被直接踢出结果集——这和你要的逻辑完全相反。

正确的解决方案

你的需求其实分两部分:

  1. 筛选标题:只保留所有关联了练习的标题(不管有没有测验/竞赛),排除所有没练习的标题(不管有没有其他关联)
  2. 控制关联加载:对于保留的标题,只加载练习数据,不加载测验和竞赛数据

修改后的代码如下:

public function index(Request $request): JsonResponse
{
    $user = User::find(auth()->id());
    
    // ... 其他请求处理代码(比如获取$limit等)

    // 根据用户状态,决定要加载的关联关系
    $withRelations = ['practices'];
    if ($user->is_accepted) {
        // 已通过的用户可以加载所有关联
        $withRelations = array_merge($withRelations, ['quizzes', 'contests']);
    }

    $titlesQuery = Title::with($withRelations);

    if (!$user->is_accepted) {
        // 未通过用户:只保留有练习的标题(不管有没有测验/竞赛)
        $titlesQuery->whereHas('practices');
    }

    // 额外的筛选条件和搜索逻辑

    return $titlesQuery->paginate($limit);
}

代码说明

  • 筛选逻辑:当用户未通过时,只用whereHas('practices'),意思是「只要标题有至少一个练习关联就保留」——这样Title A(有练习+测验)、Title B(只有练习)都会被选中,而Title C(无练习)会被排除,完全符合你的示例需求。
  • 关联加载:通过动态设置$withRelations,确保未通过用户的结果里只包含练习数据,不会带出测验或竞赛,完美实现你要的「返回练习、排除测验」的效果。

验证你的示例场景

  • Title A(有练习+测验):会被筛选出来,返回结果只包含它的练习数据
  • Title B(只有练习):正常返回,包含练习数据
  • Title C(有测验/竞赛,无练习):被排除在结果外
    完全匹配你预期的输出!

如果之后需要调整其他筛选规则,比如某些练习需要额外过滤,直接在whereHas('practices')里加闭包条件即可,不影响核心逻辑。

备注:内容来源于stack exchange,提问作者DigitalBraine

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 08:49:35