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

