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

Laravel动态生成表单并将结构化JSON数组存入MySQL的driver表

解决方案:Laravel动态表单处理JSON字段存储与更新

一、表单端:构建动态可添加/删除的字段组

要生成对应JSON数组的结构,表单字段必须使用数组命名格式(如prices[][type]),这样提交后会自动生成二维数组,完美匹配JSON对象数组的结构。

1. Blade模板基础结构

包含静态字段(姓名、地址)和动态价格字段组,支持编辑时回显已有数据:

<form method="POST" action="{{ isset($driver) ? route('drivers.update', $driver) : route('drivers.store') }}">
    @csrf
    @if(isset($driver))
        @method('PUT')
    @endif

    <!-- 静态字段 -->
    <div>
        <label>司机姓名</label>
        <input type="text" name="name" value="{{ old('name', $driver->Name ?? '') }}" required>
    </div>
    <div>
        <label>地址</label>
        <input type="text" name="address" value="{{ old('address', $driver->Address ?? '') }}" required>
    </div>

    <!-- 动态价格组容器 -->
    <div id="price-groups">
        @if(isset($driver) && !empty($driver->Price))
            @foreach(json_decode($driver->Price, true) as $index => $price)
                <div class="price-group">
                    <button type="button" class="remove-group">删除</button>
                    <div>
                        <label>类型</label>
                        <input type="text" name="prices[{{ $index }}][type]" value="{{ old('prices.'.$index.'.type', $price['type']) }}" required>
                    </div>
                    <div>
                        <label>每公里价格</label>
                        <input type="number" step="0.01" name="prices[{{ $index }}][price_km]" value="{{ old('prices.'.$index.'.price_km', $price['price_km']) }}" required>
                    </div>
                    <div>
                        <label>尺寸</label>
                        <input type="text" name="prices[{{ $index }}][size]" value="{{ old('prices.'.$index.'.size', $price['size']) }}" required>
                    </div>
                    <div>
                        <label>数量</label>
                        <input type="number" name="prices[{{ $index }}][quantity]" value="{{ old('prices.'.$index.'.quantity', $price['quantity']) }}" required>
                    </div>
                    <div>
                        <label>重量</label>
                        <input type="number" step="0.01" name="prices[{{ $index }}][weight]" value="{{ old('prices.'.$index.'.weight', $price['weight']) }}" required>
                    </div>
                </div>
            @endforeach
        @else
            <!-- 默认显示一组空字段 -->
            <div class="price-group">
                <button type="button" class="remove-group">删除</button>
                <div>
                    <label>类型</label>
                    <input type="text" name="prices[0][type]" required>
                </div>
                <div>
                    <label>每公里价格</label>
                    <input type="number" step="0.01" name="prices[0][price_km]" required>
                </div>
                <div>
                    <label>尺寸</label>
                    <input type="text" name="prices[0][size]" required>
                </div>
                <div>
                    <label>数量</label>
                    <input type="number" name="prices[0][quantity]" required>
                </div>
                <div>
                    <label>重量</label>
                    <input type="number" step="0.01" name="prices[0][weight]" required>
                </div>
            </div>
        @endif
    </div>

    <!-- 添加字段组按钮 -->
    <button type="button" id="add-price-group">添加价格组</button>

    <!-- 提交按钮 -->
    <button type="submit">保存</button>
</form>

2. JavaScript实现动态增删字段组

处理字段组的添加、删除,并在删除后重新索引字段名,避免数组断层:

document.addEventListener('DOMContentLoaded', function() {
    const addButton = document.getElementById('add-price-group');
    const container = document.getElementById('price-groups');

    // 添加新字段组
    addButton.addEventListener('click', function() {
        const groupCount = container.querySelectorAll('.price-group').length;
        const newGroup = document.createElement('div');
        newGroup.className = 'price-group';
        newGroup.innerHTML = `
            <button type="button" class="remove-group">删除</button>
            <div>
                <label>类型</label>
                <input type="text" name="prices[${groupCount}][type]" required>
            </div>
            <div>
                <label>每公里价格</label>
                <input type="number" step="0.01" name="prices[${groupCount}][price_km]" required>
            </div>
            <div>
                <label>尺寸</label>
                <input type="text" name="prices[${groupCount}][size]" required>
            </div>
            <div>
                <label>数量</label>
                <input type="number" name="prices[${groupCount}][quantity]" required>
            </div>
            <div>
                <label>重量</label>
                <input type="number" step="0.01" name="prices[${groupCount}][weight]" required>
            </div>
        `;
        container.appendChild(newGroup);
        bindRemoveEvent(newGroup.querySelector('.remove-group'));
    });

    // 初始化已有删除按钮事件
    document.querySelectorAll('.remove-group').forEach(bindRemoveEvent);

    // 绑定删除事件的通用函数
    function bindRemoveEvent(button) {
        button.addEventListener('click', function() {
            this.parentElement.remove();
            reindexPriceGroups();
        });
    }

    // 重新索引字段组的name属性
    function reindexPriceGroups() {
        container.querySelectorAll('.price-group').forEach((group, index) => {
            group.querySelectorAll('input').forEach(input => {
                input.setAttribute('name', input.getAttribute('name').replace(/prices\[\d+\]/, `prices[${index}]`));
            });
        });
    }
});

二、后端处理:接收数据并转为JSON存储

1. 验证与存储逻辑

在Controller中验证表单数据,将二维数组转为JSON字符串存入数据库:

public function store(Request $request)
{
    $validated = $request->validate([
        'name' => 'required|string',
        'address' => 'required|string',
        'prices' => 'required|array',
        'prices.*.type' => 'required|string',
        'prices.*.price_km' => 'required|numeric',
        'prices.*.size' => 'required|string',
        'prices.*.quantity' => 'required|integer',
        'prices.*.weight' => 'required|numeric',
    ]);

    // 转换为JSON格式
    $validated['Price'] = json_encode($validated['prices']);
    unset($validated['prices']);

    Driver::create($validated);
    return redirect()->route('drivers.index')->with('success', '司机信息已添加');
}

2. 更新逻辑

更新时逻辑与存储一致,只需替换为update方法:

public function update(Request $request, Driver $driver)
{
    $validated = $request->validate([
        // 验证规则同store方法
    ]);

    $validated['Price'] = json_encode($validated['prices']);
    unset($validated['prices']);

    $driver->update($validated);
    return redirect()->route('drivers.index')->with('success', '司机信息已更新');
}

三、模型优化(推荐)

在Driver模型中定义属性转换,自动处理JSON字段的序列化/反序列化,无需手动调用json_encode:

class Driver extends Model
{
    protected $fillable = ['Name', 'Address', 'Price'];

    // 自动转换JSON字段为数组
    protected $casts = [
        'Price' => 'array',
    ];
}

此时Controller代码可简化为:

// 存储
$driver = Driver::create([
    'Name' => $validated['name'],
    'Address' => $validated['address'],
    'Price' => $validated['prices'], // 直接赋值数组,模型自动转JSON
]);

// 更新
$driver->Price = $validated['prices'];
$driver->save();

关键注意点

  • 表单字段命名必须使用prices[索引][属性]格式,确保提交后得到二维数组。
  • 删除字段组后必须重新索引,避免前端回显或后端处理时出现数组断层。
  • 模型属性转换能大幅简化代码,减少手动序列化的出错概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 07:45:52