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

PHP表单提交多组数据如何插入多条数据库记录

问题描述

我不知道如何向数据库插入多条关联记录。我搭建的表单包含两组结构完全相同的赛事输入行,后续计划使用JavaScript实现动态新增输入行的功能,现阶段先通过重复编写HTML代码的方式完成功能验证。

现有表单代码

<form class="formAddCoupon" action="/?dashboard=coupon" method="post">
    <div class="windowEvents">
        <div class="row">
            <input type="text" class="teamInput" name="team1">
            <input type="text" class="teamInput" name="team2">
            <select name="score" id="selectOptions">
                <option value="Liczba bramek">Liczba bramek</option>
                <option value="Liczba rzutów rożnych">
                        Liczba rzutów rożnych</option>
                <option value="Ilość kartek">Liczba kartek</option>
                <option value="Wygrana drużyny">Wygrana drużyny</option>
                <option value="Wygrana drużyny lub połowy">
                        Wygrana drużyny lub połowy</option>
                <option value="Wygrana połowy">Wygrana jednej z połów</option>
                <option value="Hendicap">Hendicap</option>
                <option value="Ilość punktów">Liczba punktów</option>
            </select>
            <input type="text" class="scoreInput" name="type_score">
        </div>
        <div class="row">
            <input type="text" class="teamInput" name="team1">
            <input type="text" class="teamInput" name="team2">
            <select name="score" id="selectOptions">
                <option value="Liczba bramek">Liczba bramek</option>
                <option value="Liczba rzutów rożnych">
                        Liczba rzutów rożnych</option>
                <option value="Ilość kartek">Liczba kartek</option>
                <option value="Wygrana drużyny">Wygrana drużyny</option>
                <option value="Wygrana drużyny lub połowy">
                        Wygrana drużyny lub połowy</option>
                <option value="Wygrana połowy">Wygrana jednej z połów</option>
                <option value="Hendicap">Hendicap</option>
                <option value="Ilość punktów">Liczba punktów</option>
            </select>
            <input type="text" class="scoreInput" name="type_score">
        </div>
    </div>
    <div class="valueAmountRow">
        <input
            type="number"
            name="bid_amount"
            id="bid_amount"
            placeholder="Stawka"
            min="0"
            step="any"
        >
        <input
            type="number"
            name="winner_amount"
            id="winner_amount"
            placeholder="Wygrana"
            min="0"
            step="any"
        >
        <input type="submit" value="Dodaj">
    </div>
</form>

现有Controller.php处理逻辑

$data = $this->getRequestPost();
if(!empty($data)) {
    $couponData = [
        'bid_amount' => $data['bid_amount'],
        'winner_amount' => $data['winner_amount'],
        'team1' => $data['team1'],
        'team2' => $data['team2'],
        'score' => $data['score'],
        'type_score' => $data['type_score'],
    ];
    $this->db->createCoupon($couponData);
}

现有Database.php写入逻辑

public function createCoupon(array $data)
{
    try {
        $this->createNewCoupon($data);
    } catch(Throwable $e) {
        dump($e);
        throw new StorageException('Nie udało się wysłać kuponu', 400, $e);
    }
}

private function createNewCoupon($data): void
{
        $nr_coupon =  trim($this->connection->quote($this->checkIssetCode()));
        $created = $this->connection->quote(date('Y-m-d H:i:s'));
        $bid_amount = trim($this->connection->quote($data['bid_amount']));
        $winner_amount = trim($this->connection->quote($data['winner_amount']));
        $team1 = trim($this->connection->quote($data['team1']));
        $team2 = trim($this->connection->quote($data['team2']));
        $score = trim($this->connection->quote($data['score']));
        $typeScore = trim($this->connection->quote($data['type_score']));

        $insertNumberCoupon = "INSERT INTO
            coupon_number(
                nr_coupon,
                created,
                bid_amount,
                winner_amount
            )
            VALUES(
                $nr_coupon,
                $created,
                $bid_amount,
                $winner_amount
            )";

        $sql_create_number_coupon = $this->connection->prepare($insertNumberCoupon);
        if($sql_create_number_coupon)
        {
            $sql_create_number_coupon->execute();
            $id = (int) $this->connection->lastInsertId();
            $addEvent = "INSERT INTO
                events_at_coupon(
                    number_id_coupon,
                    team1,
                    team2,
                    score,
                    type_score
                )
                VALUES($id,$team1,$team2,$score,$typeScore)";

            $sql_add_event_to_table = $this->connection->prepare($addEvent);
            if($sql_add_event_to_table)
            {
                $sql_add_event_to_table->execute();
            }
        }
}

当前问题

提交表单后数据库仅会写入1条赛事记录,已确认问题源于同名字段提交时值覆盖、后端仅执行单条插入逻辑,需要修改实现多条关联赛事记录的正确写入。


修改方案

分三个部分调整,从前端到后端全链路适配多赛事提交场景,同时兼容后续JS动态新增行的需求。

1. 调整前端表单字段命名

把所有赛事相关的同名字段改成数组格式,提交时PHP会自动把同组字段解析为数组,不会出现值覆盖问题。注意原代码中两个select复用同一个id不符合HTML规范,改为class属性即可,后续动态新增行也不会出现id重复问题。
修改后的单条赛事行结构示例:

<div class="row">
    <input type="text" class="teamInput" name="events[][team1]">
    <input type="text" class="teamInput" name="events[][team2]">
    <select name="events[][score]" class="selectOptions">
        <!-- 选项内容保持不变 -->
    </select>
    <input type="text" class="scoreInput" name="events[][type_score]">
</div>

后续用JS动态新增行时,直接复制该段结构插入即可,不需要额外调整字段名,提交时会自动按顺序组成赛事数组

2. 调整控制器接收逻辑

不再单独提取单个赛事字段,直接把提交上来的赛事数组和优惠券公共字段一起传给数据库层,可增加基础校验避免空数据提交:

$data = $this->getRequestPost();
if(!empty($data)) {
    $couponData = [
        'bid_amount' => $data['bid_amount'],
        'winner_amount' => $data['winner_amount'],
        'events' => $data['events'] ?? []
    ];
    if(empty($couponData['events'])) {
        // 此处添加业务校验失败的提示逻辑即可
    }
    $this->db->createCoupon($couponData);
}

3. 调整数据库层写入逻辑

优惠券主表coupon_number仅需插入1次,拿到自增ID之后,循环遍历所有赛事数据,逐条插入到events_at_coupon关联赛事表即可。同步改用参数绑定的预处理语句,避免SQL注入风险,且SQL仅需预处理一次,循环执行的性能更好。
修改后的createNewCoupon方法:

private function createNewCoupon($data): void
{
    // 插入优惠券主记录
    $nr_coupon = $this->checkIssetCode();
    $created = date('Y-m-d H:i:s');
    $insertCouponSql = "INSERT INTO coupon_number(nr_coupon, created, bid_amount, winner_amount)
        VALUES(:nr_coupon, :created, :bid_amount, :winner_amount)";
    $couponStmt = $this->connection->prepare($insertCouponSql);
    $couponStmt->execute([
        ':nr_coupon' => $nr_coupon,
        ':created' => $created,
        ':bid_amount' => $data['bid_amount'],
        ':winner_amount' => $data['winner_amount']
    ]);
    $couponId = (int)$this->connection->lastInsertId();

    // 预处理赛事插入SQL,复用语句对象循环执行
    $insertEventSql = "INSERT INTO events_at_coupon(number_id_coupon, team1, team2, score, type_score)
        VALUES(:coupon_id, :team1, :team2, :score, :type_score)";
    $eventStmt = $this->connection->prepare($insertEventSql);
    
    // 循环写入所有赛事
    foreach($data['events'] as $event) {
        // 跳过空的无效行
        if(empty($event['team1']) || empty($event['team2'])) continue;
        $eventStmt->execute([
            ':coupon_id' => $couponId,
            ':team1' => trim($event['team1']),
            ':team2' => trim($event['team2']),
            ':score' => $event['score'],
            ':type_score' => trim($event['type_score'])
        ]);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 17:27:22