Laravel集成Dropzone提交表单时Masseur模型重复入库问题
问题描述
我有两个模型:Masseur和MasseurImage,二者通过belongsTo()关联。我搭建了一个包含基础输入项和Dropzone图片上传组件的表单,预期提交「创建新按摩师」表单时,在数据库中创建一条Masseur记录,并根据Dropzone中添加的图片数量创建多条MasseurImage记录。
目前所有功能基本正常,但表单提交后,Masseur模型会被重复存入数据库两次。
视图代码
<form method="post" id="etCreateMasseurForm" action="{{ route('masseur.add') }}" name="etCreateMasseurForm" enctype="multipart/form-data"> @csrf <input type="hidden" name="masseur_id" id="masseur_id"> <div class="form-group mb-3"> <label class="form-label" for="nickname">Becenév</label> <input class="form-control" type="text" name="nickname" id="nickname" required> </div> <div class="form-group mb-3"> <label class="form-label" for="name">Név</label> <input class="form-control" type="text" name="name" id="name" required> </div> <div id="dropzoneDragArea" class="dropzone dz-default dz-message dropzoneDragArea form-control"></div> </form>
控制器代码
public function store(Request $request) { $masseur = Masseur::create($request->all()); if ($request->hasFile('file')) { foreach ($request->file('file') as $file) { $filename = $file->store('public/files/'.$masseur->id); MasseurImage::create([ 'masseur_id' => $masseur->id, 'filename' => $filename ]); } } return response()->json(['status' => "success", 'masseur_id'=> $masseur->id]); }
JavaScript代码
Dropzone.autoDiscover = false; let token = $('meta[name="csrf-token"]').attr('content'); $(function () { var myDropzone = new Dropzone("div#dropzoneDragArea", { paramName: "file", url: "{{ route('masseur.add') }}", addRemoveLinks: true, autoProcessQueue: false, uploadMultiple: true, parallelUploads: 100, maxFiles: 100, params: { _token: token }, init: function () { var myDropzone = this; $("form[name='etCreateMasseurForm']").submit(function (event) { event.preventDefault(); URL = $("#etCreateMasseurForm").attr('action'); formData = $('#etCreateMasseurForm').serialize(); $.ajax({ type: 'POST', url: URL, data: formData, success: function (result) { if (result.status == "success") { var masseur_id = result.masseur_id; $("#masseur_id").val(masseur_id); myDropzone.processQueue(); } else { console.log("error"); } } }); }); this.on('sending', function (file, xhr, formData) { let masseur_id = document.getElementById('masseur_id').value; formData.append('masseur_id', masseur_id); }); this.on("success", function (file, response) { window.location.href = "/"; }); this.on("queuecomplete", function () { }); } }); });
路由代码
Route::post('store-masseur', [MasseurController::class, 'store'])->name('masseur.add');
问题原因
重复创建Masseur的核心原因是两次调用了同一个store接口:
- 表单提交时的AJAX请求会触发一次
store方法,创建第一条Masseur记录 - Dropzone执行
processQueue时,会再次向同一个store路由发送请求,而控制器代码不管请求是否包含图片,都会先执行Masseur::create($request->all()),因此会创建第二条重复的Masseur记录
解决方案
方案一:在原控制器中增加判断逻辑
修改store方法,通过请求中的masseur_id判断是否为图片上传请求,仅在无masseur_id时创建新的Masseur:
public function store(Request $request) { // 若请求携带masseur_id,说明是上传图片的请求,直接获取已有记录 if ($request->masseur_id) { $masseur = Masseur::findOrFail($request->masseur_id); } else { // 无masseur_id则创建新的Masseur $masseur = Masseur::create($request->all()); } if ($request->hasFile('file')) { foreach ($request->file('file') as $file) { $filename = $file->store('public/files/'.$masseur->id); MasseurImage::create([ 'masseur_id' => $masseur->id, 'filename' => $filename ]); } } return response()->json(['status' => "success", 'masseur_id'=> $masseur->id]); }
方案二:拆分路由和控制器方法(推荐,逻辑更清晰)
将创建Masseur和上传图片的逻辑拆分到不同的接口,避免冲突:
1. 修改路由
// 创建按摩师接口 Route::post('masseur', [MasseurController::class, 'createMasseur'])->name('masseur.create'); // 按摩师图片上传接口 Route::post('masseur/{masseur}/images', [MasseurController::class, 'uploadImages'])->name('masseur.images.upload');
2. 新增控制器方法
// 创建按摩师 public function createMasseur(Request $request) { $masseur = Masseur::create($request->all()); return response()->json(['status' => "success", 'masseur_id'=> $masseur->id]); } // 上传按摩师图片 public function uploadImages(Request $request, Masseur $masseur) { if ($request->hasFile('file')) { foreach ($request->file('file') as $file) { $filename = $file->store('public/files/'.$masseur->id); MasseurImage::create([ 'masseur_id' => $masseur->id, 'filename' => $filename ]); } } return response()->json(['status' => "success"]); }
3. 修改前端JavaScript代码
调整Dropzone的上传URL,并在创建Masseur成功后替换URL中的按摩师ID:
Dropzone.autoDiscover = false; let token = $('meta[name="csrf-token"]').attr('content'); $(function () { var myDropzone = new Dropzone("div#dropzoneDragArea", { paramName: "file", // 先设置占位符URL url: "{{ route('masseur.images.upload', ['masseur' => '']) }}", addRemoveLinks: true, autoProcessQueue: false, uploadMultiple: true, parallelUploads: 100, maxFiles: 100, params: { _token: token }, init: function () { var myDropzone = this; $("form[name='etCreateMasseurForm']").submit(function (event) { event.preventDefault(); // 修改为创建按摩师的接口 URL = "{{ route('masseur.create') }}"; formData = $('#etCreateMasseurForm').serialize(); $.ajax({ type: 'POST', url: URL, data: formData, success: function (result) { if (result.status == "success") { var masseur_id = result.masseur_id; $("#masseur_id").val(masseur_id); // 替换Dropzone的上传URL为实际的图片上传接口 myDropzone.options.url = "{{ route('masseur.images.upload', ['masseur' => '']) }}" + masseur_id; myDropzone.processQueue(); } else { console.log("error"); } } }); }); this.on("success", function (file, response) { window.location.href = "/"; }); this.on("queuecomplete", function () { }); } }); });
同时修改表单的action属性:
<form method="post" id="etCreateMasseurForm" action="{{ route('masseur.create') }}" name="etCreateMasseurForm" enctype="multipart/form-data">
内容的提问来源于stack exchange,提问作者ErikTailor
相关产品推荐
相关产品推荐

