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

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.');
}

验证步骤

  1. 确保Test模型的$fillable包含score字段
  2. 应用题目ID类型转换的修改
  3. 添加调试日志并提交正确答案
  4. 查看数据库中tests表的score字段是否正确更新
  5. 查看日志确认每道题的isCorrect标记是否符合预期

内容的提问来源于stack exchange,提问作者Josh Dei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 01:20:55