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
相关产品推荐
相关产品推荐

