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

Laravel 10中AlpineJS动态表单复选框存库状态错误求助

动态数组表单复选框选中状态与数据库存储不一致问题排查

在Laravel 10项目中使用AlpineJS实现包含文本输入框与复选框的动态数组表单,选中选项C作为正确答案后,保存到数据库却显示选项A为正确答案。以下是问题排查与修复方案:


原前端HTML代码

<div class="mt-4 w-full" x-data="handler()">
    <template class=" grid grid-cols-1 gap-6" x-for="(field, index) in fields" :key="index">
         <div class="flex w-full">
              <div class="font-semibold">
                   <x-input-label for="name" :value="__('Choice')" />
                   <input type="text" class="w-12 border-none focus:border-none hover:border-none" name="question_option_id[]" x-bind:value="String.fromCharCode(65 + index)" readonly>
              </div>
              <div class="px-2">
                   <x-input-label for="name" :value="__('Answer')" />
                   <x-text-input x-bind="field.name" type="text" :name="'name[]'" />
              </div>
              <div class="px-2">
                   <div class=" items-center">
                        <x-input-label for="name" :value="__('Correct Answers')" />
                               <label class="relative flex cursor-pointer items-center rounded-full p-3"
                                   for="check" data-ripple-dark="true">
                                   <x-text-input x-bind="field.check[]" type="checkbox"
                                       :name="'check[]'"
                                        value="yes"
                                        class="before:content[''] peer relative h-5 w-5 cursor-pointer appearance-none rounded-md border border-blue-gray-200 transition-all before:absolute before:top-2/4 before:left-2/4 before:block before:h-12 before:w-12 before:-translate-y-2/4 before:-translate-x-2/4 before:rounded-full before:bg-blue-gray-500 before:opacity-0 before:transition-opacity checked:border-pink-500 checked:bg-pink-500 checked:before:bg-pink-500 hover:before:opacity-10"
                                          id="check" />
                                </label>
                   </div>
              </div>
              <div class="px-2">
                  <x-danger-button type="button" @click="removeField(index)"
                        class="mt-4 ">
                           <i class="fa-regular fa-trash-can text-xl"></i>
                  </x-danger-button>
              </div>
           </div>
       </template>
       <div colspan="4" class="mt-2 text-right">
            <x-sky-button type="button" @click="addNewField()">
                   <i class="fa-solid fa-plus"></i> &nbsp; Add Answer
            </x-sky-button>
       </div>
   </div>

原AlpineJS脚本

function handler() {
    return {
        fields: [],
        addNewField() {
            this.fields.push({
                name: '',
                check: ''
            });
        },
        removeField(index) {
            this.fields.splice(index, 1);
        }
    }
}

原Laravel控制器代码

public function storeQuestion(Request $request)
{
    $request->validate([
        'intructors' => 'required',
        'question' => 'required',
        'name' => 'required|array', // Validate each answer option
    ]);

    // Create a new exam question
    $examQuestion = new Questions();
    $examQuestion->user_id = $request->intructors;
    $examQuestion->exam_id = $request->exams_id;
    $examQuestion->name = $request->question;

    $examQuestion->save();

    // Save the answer options and their correct statuses
    $options = $request->name;
    $correctAnswers = $request->check;
    foreach ($options as $index => $option) {
        
        //check for checkbox got checked   
        $isCorrect = isset($correctAnswers[$index]) ? 'yes' : 'no';

        // get question id on model question 
        $getQuestion = Questions::where('name', $request->question)->first();

        $examQuestionOption = new QuestionsOption();
        $examQuestionOption->create([
            'user_id' => $request->intructors,
            'question_id' => $getQuestion->id,
            'name' => $option,
            'is_correct_answer' => $isCorrect,
        ]);
    }

    // Redirect or return a response as needed
    return redirect()->route('admin.exams')->with('message', 'Question created successfully');
}

错误原因分析

  1. 复选框请求索引不匹配
    浏览器提交表单时,未选中的复选框不会被包含在请求数据中。比如选中第3个选项(索引2),$correctAnswers数组仅包含1个元素且索引为0,循环时isset($correctAnswers[$index])会误判第0个选项为正确答案。

  2. 前端绑定与属性错误

    • 复选框的x-bind="field.check[]"写法错误,无法正确同步选中状态;
    • 所有复选框使用相同的id="check",违反HTML规范,可能导致交互异常;
    • 复选框name="check[]"未绑定索引,无法对应到具体选项。
  3. 控制器冗余查询
    循环内重复执行Questions::where('name', $request->question)->first(),既浪费性能,还可能因同名问题获取错误的Question ID。


修复方案

修复后前端HTML代码(关键部分)

<template class="grid grid-cols-1 gap-6" x-for="(field, index) in fields" :key="index">
    <!-- 其他部分保持不变 -->
    <div class="px-2">
        <div class="items-center">
            <x-input-label :for="'check-' + index" :value="__('Correct Answers')" />
            <label class="relative flex cursor-pointer items-center rounded-full p-3"
                   :for="'check-' + index" data-ripple-dark="true">
                <x-text-input x-model="field.check" type="checkbox"
                             :name="'check[' + index + ']'"
                             value="yes"
                             class="before:content[''] peer relative h-5 w-5 cursor-pointer appearance-none rounded-md border border-blue-gray-200 transition-all before:absolute before:top-2/4 before:left-2/4 before:block before:h-12 before:w-12 before:-translate-y-2/4 before:-translate-x-2/4 before:rounded-full before:bg-blue-gray-500 before:opacity-0 before:transition-opacity checked:border-pink-500 checked:bg-pink-500 checked:before:bg-pink-500 hover:before:opacity-10"
                             :id="'check-' + index" />
            </label>
        </div>
    </div>
    <!-- 其他部分保持不变 -->
</template>

修复后AlpineJS脚本

function handler() {
    return {
        fields: [],
        addNewField() {
            this.fields.push({
                name: '',
                check: false // 用布尔值同步选中状态更直观
            });
        },
        removeField(index) {
            this.fields.splice(index, 1);
        }
    }
}

修复后Laravel控制器代码

public function storeQuestion(Request $request)
{
    $request->validate([
        'intructors' => 'required',
        'question' => 'required',
        'name' => 'required|array',
        'name.*' => 'required', // 验证每个选项的内容不能为空
    ]);

    // 直接创建问题并获取ID,避免重复查询
    $examQuestion = Questions::create([
        'user_id' => $request->intructors,
        'exam_id' => $request->exams_id,
        'name' => $request->question,
    ]);

    $options = $request->name;
    $correctAnswers = $request->check ?? [];

    // 初始化正确答案数组,默认全部为'no'
    $isCorrectArray = array_fill(0, count($options), 'no');
    
    // 根据提交的复选框索引,标记对应选项为正确
    foreach ($correctAnswers as $index => $value) {
        if (isset($isCorrectArray[$index])) {
            $isCorrectArray[$index] = 'yes';
        }
    }

    // 批量创建选项,提升性能
    $optionsData = [];
    foreach ($options as $index => $option) {
        $optionsData[] = [
            'user_id' => $request->intructors,
            'question_id' => $examQuestion->id,
            'name' => $option,
            'is_correct_answer' => $isCorrectArray[$index],
        ];
    }
    QuestionsOption::insert($optionsData);

    return redirect()->route('admin.exams')->with('message', 'Question created successfully');
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 01:49:58