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

Laravel动态测验表单:如何确保所有单选框必选并通过验证?

问题背景

我有多个测验系统,每个测验包含多道题目,题型分为MCQ(单项选择题)、判断题和自由文本题。相关数据库表结构如下:

questions_types表

id | type
1  | MCQ
2  | True or false
3  | Text

questions表

id | question_en | question_fr | type_id
1  | question 1  | question 1  | 1
2  | question 2  | question 2  | 2
3  | question 3  | question 3  | 3

answers表

id | answer_en | answer_fr  | question_id
1  | answer 1  | répondre 1 | 1
2  | answer 2  | répondre 2 | 1
3  | answer 3  | répondre 3 | 1
4  | true      | vrai       | 2
5  | false     | faux       | 2

对应的Laravel Blade模板代码如下:

@foreach($questions as $question)
    <h4>{{ $question->question_en }}</h4>
    @if($question->type_id != 3)
        @foreach($question->answers as $answer)
        <input type="radio" name="answers[{{ $question->id }}]" value="{{ $answer->answer_en }}" @if(old("answers." . $question->id) == $answer->answer_en) checked="checked" @endif>
        @endforeach
    @else
        <textarea name="answers[{{ $question->id }}]">{{ old("answers." . $question->id) }}</textarea>
    @endif
@endforeach

模板生成的前端输出为:

<h4>question 1</h4>
<input type="radio" name="answers[1]" value="answer 1">
<input type="radio" name="answers[1]" value="answer 2">
<input type="radio" name="answers[1]" value="answer 3">

<h4>question 2</h4>
<input type="radio" name="answers[2]" value="true">
<input type="radio" name="answers[2]" value="false">

<h4>question 3</h4>
<textarea name="answers[3]"></textarea>

当前使用的Form Request验证规则如下:

public function rules()
{
    return [
        'answers' => 'required|array',
        'answers.*' => 'required',
    ];
}

问题描述

上述验证规则只能验证文本域的必填项,当单选框未被选中时,表单依然能通过验证。由于测验是动态生成的,无法指定固定的题目数量或选项值进行验证,请问如何实现所有单选框必须被选中的验证逻辑?


解决方案

核心思路是动态获取当前测验的所有题目,根据题型生成对应验证规则。单选/判断题未选中时,answers数组不会包含对应题目ID的键,因此需要确保每个非文本题型的题目ID都存在于answers数组中,且值为有效选项。

步骤1:获取当前测验的题目列表

假设测验通过路由参数(如/quizzes/{quiz}/submit)传递ID,在Form Request中预加载该测验下的所有题目及对应选项:

protected function prepareForValidation()
{
    // 从路由获取测验ID,根据实际路由调整参数名
    $quizId = $this->route('quiz');
    // 预加载题目选项,避免N+1查询
    $this->merge([
        'quiz_questions' => \App\Models\Question::where('quiz_id', $quizId)
            ->with('answers')
            ->get()
    ]);
}

步骤2:动态构建验证规则

遍历所有题目,为不同题型设置针对性规则:

  • 非文本题型(单选、判断):必须存在对应answers.{question_id}键,且值属于该题目的有效选项
  • 文本题型:验证必填即可

修改Form Request的rules方法:

use Illuminate\Validation\Rule;

public function rules()
{
    $rules = [
        'answers' => 'required|array',
    ];

    /** @var \Illuminate\Database\Eloquent\Collection $questions */
    $questions = $this->get('quiz_questions');

    foreach ($questions as $question) {
        $field = "answers.{$question->id}";
        if ($question->type_id != 3) {
            // 收集该题目的所有有效选项值
            $validAnswers = $question->answers->pluck('answer_en')->toArray();
            $rules[$field] = [
                'required',
                Rule::in($validAnswers), // 防止提交非法选项
            ];
        } else {
            $rules[$field] = 'required|string';
        }
    }

    return $rules;
}

步骤3:自定义友好验证消息(可选)

为了让错误提示更清晰,添加messages方法:

public function messages()
{
    $messages = [];
    /** @var \Illuminate\Database\Eloquent\Collection $questions */
    $questions = $this->get('quiz_questions');

    foreach ($questions as $question) {
        $field = "answers.{$question->id}";
        if ($question->type_id != 3) {
            $messages["{$field}.required"] = "请选择题目「{$question->question_en}」的答案";
            $messages["{$field}.in"] = "题目「{$question->question_en}」的选项无效";
        } else {
            $messages["{$field}.required"] = "请填写题目「{$question->question_en}」的答案";
        }
    }

    return $messages;
}

关键说明

  • 单选框未选中时,answers数组不会生成对应键,required规则会直接触发验证失败,解决了原规则的漏洞
  • Rule::in($validAnswers)可防止用户手动修改表单提交非法选项
  • 预加载题目和选项能避免数据库查询性能问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:01:03