Laravel Eloquent查询优化:需包含带练习的标题(即使同时关联测验/竞赛),仅排除无练习的测验/竞赛类标题
Laravel Eloquent查询优化:需包含带练习的标题(即使同时关联测验/竞赛),仅排除无练习的测验/竞赛类标题
我完全get到你的问题了——你现在的查询错误地把所有带测验或竞赛的标题都排除了,但你真正想要的是:只要标题关联了练习,不管它有没有测验/竞赛,都要保留在结果里,而且返回的时候只展示练习部分;只有那些完全没练习、只有测验/竞赛的标题才需要排除。咱们来修正这个逻辑:
问题根源分析
你原来的代码里用了whereDoesntHave('quizzes')和whereDoesntHave('contests'),这两个条件的意思是「这个标题完全没有测验」和「这个标题完全没有竞赛」,所以只要标题沾了测验/竞赛的边(哪怕同时有练习),都会被直接踢出结果集——这和你要的逻辑完全相反。
正确的解决方案
你的需求其实分两部分:
- 筛选标题:只保留所有关联了练习的标题(不管有没有测验/竞赛),排除所有没练习的标题(不管有没有其他关联)
- 控制关联加载:对于保留的标题,只加载练习数据,不加载测验和竞赛数据
修改后的代码如下:
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
相关产品推荐
相关产品推荐

