Laravel课程考试提交控制器分数始终为0的问题排查
Laravel考试提交分数始终为0的排查与修复
问题描述
我编写了一个Laravel控制器方法处理用户课程考试提交,预期实现:根据提交答案计算分数、将分数存入数据库并记录详细答题情况,但当前异常为:无论提交答案正确与否,数据库中保存的分数始终为0。
代码执行流程:
- 根据冷却时间检查用户是否可重考
- 验证输入参数(course_id和answers)
- 创建初始分数为0的考试记录
- 遍历已答题目,对比提交答案与正确答案
- 答案正确则增加分数
- 保存带正确性标记的答题记录
- 更新考试记录的最终分数
- 提交事务并跳转至结果页面
具体代码:
public function submitTest(Request $request) { $user = Auth::user(); $courseId = $request->input('course_id'); $cooldownHours = 2; $lastTest = Test::where('user_id', $user->id) ->where('course_id', $courseId)->latest() ->first(); if ($lastTest) { $hoursSinceLastTest = now()->diffInHours($lastTest->created_at); if ($hoursSinceLastTest < $cooldownHours) { $remaining = $cooldownHours - $hoursSinceLastTest; return redirect()->back() ->withErrors("You can retake this test in {$remaining} hour(s)."); } } $request->validate([ 'course_id' => 'required|exists:courses,id', 'answers' => 'required|array', ]); $answers = $request->input('answers'); $score = 0; DB::beginTransaction(); try { $totalQuestions = Question::where('course_id', $courseId)->count(); $existingTest = Test::where('user_id', $user->id) ->where('course_id', $courseId)->first(); if ($existingTest) { return redirect()->back() ->withErrors('You have already submitted this test.'); } $test = Test::create([ 'user_id' => $user->id, 'course_id' => $courseId, 'score' => 0, 'total_questions' => $totalQuestions, ]); $answeredQuestionIds = array_keys($answers); $answeredQuestions = Question::whereIn('id', $answeredQuestionIds)->get(); foreach ($answeredQuestions as $question) { $questionId = $question->id; $userRawAnswer = $answers[$questionId] ?? ''; $userAnswer = strtoupper(trim($userRawAnswer)); $isCorrect = false; if ($question->type === 'mcq') { $isCorrect = strcasecmp($userAnswer, $question->answer) === 0; } elseif ($question->type === 'fill') { $isCorrect = strcasecmp(trim($userAnswer), trim($question->answer)) === 0; } elseif ($question->type === 'truefalse') { $validTrue = ['true', 't', 'yes', 'y', '1']; $validFalse = ['false', 'f', 'no', 'n', '0']; $userNormalized = strtolower($userAnswer); $correctNormalized = strtolower(trim($question->answer)); if (in_array($correctNormalized, $validTrue)) { $isCorrect = in_array($userNormalized, $validTrue); } elseif (in_array($correctNormalized, $validFalse)) { $isCorrect = in_array($userNormalized, $validFalse); } } if ($isCorrect) { $score++; } TestAnswer::create([ 'test_id' => $test->id, 'question_id' => $questionId, 'selected_option' => $userRawAnswer, 'is_correct' => $isCorrect, ]); } $test->score = $score; $test->save(); DB::commit(); session()->forget("test_start_time_{$courseId}"); return redirect()->route('viewResult'); } catch (\Exception $e) { DB::rollBack(); Log::error('Test submission error: ' . $e->getMessage()); return redirect()->back()->withErrors('An error occurred while submitting your test. Please try again.'); } }
核心问题排查与修复
1. Test模型未允许score字段赋值更新
Laravel模型仅允许$fillable数组中声明的字段被赋值保存。如果Test模型未将score加入$fillable,即使执行$test->score = $score; $test->save();,数据库中的score字段也不会被更新。
修复:
修改Test模型,确保score在$fillable中:
class Test extends Model { protected $fillable = [ 'user_id', 'course_id', 'score', 'total_questions' ]; }
2. 题目ID类型不匹配导致未获取到题目
前端提交的answers数组的键通常是字符串类型(如"1"),而数据库中Question的id是整数类型。直接使用array_keys($answers)作为whereIn的条件,可能导致查询不到对应题目,循环不执行,score始终为0。
修复:
将题目ID转换为整数数组:
// 替换原有的answeredQuestionIds赋值 $answeredQuestionIds = array_map('intval', array_keys($answers)); $answeredQuestions = Question::whereIn('id', $answeredQuestionIds)->get();
3. 题型匹配逻辑验证
可以添加调试日志确认每道题的判断结果是否正确:
// 在判断isCorrect后添加日志 Log::info('答题验证详情', [ 'question_id' => $questionId, 'user_answer' => $userAnswer, 'correct_answer' => $question->answer, 'is_correct' => $isCorrect ]);
查看日志确认是否有正确答案被错误标记的情况,针对性调整匹配逻辑。
4. 重复考试判断逻辑冲突
当前代码先检查冷却时间允许重考,随后又判断用户是否提交过该考试(无论时间),这会导致用户只能考一次,与冷却时间逻辑矛盾。如果允许重考,应删除该判断;如果不允许重考,应删除冷却时间检查。
修复(允许重考的情况):
删除以下代码块:
$existingTest = Test::where('user_id', $user->id) ->where('course_id', $courseId)->first(); if ($existingTest) { return redirect()->back() ->withErrors('You have already submitted this test.'); }
验证步骤
- 确保
Test模型的$fillable包含score字段 - 应用题目ID类型转换的修改
- 添加调试日志并提交正确答案
- 查看数据库中
tests表的score字段是否正确更新 - 查看日志确认每道题的
isCorrect标记是否符合预期
内容的提问来源于stack exchange,提问作者Josh Dei
相关产品推荐
相关产品推荐

