Laravel中如何避免随机获取试题时出现重复?
解决Laravel随机抽取试题避免重复的方案
咱先理清楚问题:你当前的代码每次从指定章节里随机捞1道题,但完全没记录用户已经见过哪些题,所以重复概率自然很高。要解决这个问题,核心思路就是跟踪用户已经浏览/作答过的试题ID,下次查询时直接排除这些ID。
下面给你两种常用的解决方案,按需选择:
一、基于Session的临时方案(适合未登录用户)
如果你的场景允许用户不登录做题,用Session来临时存储已做试题ID就足够了,简单快捷:
public function mcq($id) { // 从Session取出已做过的试题ID,默认是空数组 $answeredIds = session()->get('answered_questions', []); // 查询当前章节下,用户没做过的试题,随机取1道 $questions = Chapter::find($id)->questions() ->whereNotIn('id', $answeredIds) ->orderByRaw("RAND()") ->paginate(1); // 如果还有未做的题,把当前题ID加入已做列表,更新Session if ($questions->isNotEmpty()) { $answeredIds[] = $questions->first()->id; session()->put('answered_questions', $answeredIds); } return view('pages.mcq')->withQuestions($questions); }
👉 优点:不需要额外改数据库,实现成本低;
👉 缺点:用户清除浏览器缓存、换设备的话,记录会丢失,适合临时刷题场景。
二、基于数据库的持久化方案(适合登录用户)
如果用户是登录状态,建议把答题记录存到数据库里,这样跨设备也能保留用户的答题进度:
第一步:创建关联表
先建一个user_answered_questions表,用来记录用户和已做试题的关联:
Schema::create('user_answered_questions', function (Blueprint $table) { $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->foreignId('question_id')->constrained()->onDelete('cascade'); $table->timestamps(); $table->unique(['user_id', 'question_id']); // 防止重复记录同一道题 });
第二步:给User模型加关联
在User.php里添加和Question的多对多关联:
public function answeredQuestions() { return $this->belongsToMany(Question::class, 'user_answered_questions'); }
第三步:修改控制器代码
public function mcq($id) { // 获取当前登录用户 $user = auth()->user(); // 取出用户已做过的所有试题ID $answeredIds = $user->answeredQuestions()->pluck('questions.id')->toArray(); // 查询章节下未做过的试题,随机取1道 $questions = Chapter::find($id)->questions() ->whereNotIn('id', $answeredIds) ->orderByRaw("RAND()") ->paginate(1); // 如果有未做的题,把这道题记录到关联表 if ($questions->isNotEmpty()) { $user->answeredQuestions()->attach($questions->first()->id); } return view('pages.mcq')->withQuestions($questions); }
👉 优点:数据持久化,用户换设备也能接着上次的进度刷题;
👉 缺点:需要额外创建数据库表,适合有用户体系的正式项目。
边界情况处理
当章节里所有题都被用户做完之后,whereNotIn会返回空结果,这时候可以给用户加个提示,或者重置答题记录:
if ($questions->isEmpty()) { // 可选:重置答题记录(比如让用户重新刷) // session()->forget('answered_questions'); // Session方案的重置 // $user->answeredQuestions()->detach(); // 数据库方案的重置 return view('pages.mcq')->with('message', '该章节所有试题已全部完成!'); }
内容的提问来源于stack exchange,提问作者Hassan
相关产品推荐
相关产品推荐

