Laravel-React商品数据更新时图片上传致请求数据为空问题
商品更新时上传图片导致Laravel控制器$request为空数组的问题
问题现象
- 提交商品更新表单时,若不选择图片,后端能正常接收并处理所有表单数据,商品信息更新成功;
- 一旦选择图片文件上传,后端控制器中的
$request会变成空数组([] // app/Http/Controllers/Merchandise/MerchandiseController.php:122),无法处理任何数据,更新流程完全中断。
前端React代码
import React from 'react'; import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; import InputError from '@/Components/InputError'; import InputLabel from '@/Components/InputLabel'; import NumberInput from '@/Components/NumberInput'; import DateInput from '@/Components/DateInput'; import PrimaryButton from '@/Components/PrimaryButton'; import SelectBoxEditId from '@/Components/SelectBoxEditId'; import Flash from '@/Components/Flash'; import TextInput from '@/Components/TextInput'; import { useForm, usePage } from '@inertiajs/inertia-react'; export default function EditMerchandise(props) { const { merchandise } = usePage().props; const { data, setData, put, processing, errors } = useForm({ name: merchandise.name || '', description: merchandise.description || '', merchandise_category_id: merchandise.merchandise_category_id || 0, price: merchandise.price || 0, date: merchandise.date || '', stock: merchandise.stock || 0, image: null, }); const onHandleChange = (event) => { setData( event.target.name, event.target.type === 'checkbox' ? event.target.checked : event.target.value ); }; const submit = (e) => { e.preventDefault(); put(route('merchandise.update', merchandise.id)); }; return ( <AuthenticatedLayout auth={props.auth} errors={props.errors}> <div className="w-full lg:max-w-6xl mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg"> {props.flash.message !== null ? <Flash message={props.flash.message} /> : <div></div>} <form onSubmit={submit}> <div> <InputLabel forInput="name" value="Name" /> <TextInput type="text" name="name" value={data.name} className="m-2 input input-bordered w-full" handleChange={onHandleChange} /> <InputError message={errors.name} className="mt-2" /> <InputLabel forInput="category" value="Category" /> <SelectBoxEditId name="merchandise_category_id" value={data.merchandise_category_id} data={props.merchandiseMaster} selected={merchandise.merchandise_master.name} id={parseInt(merchandise.merchandise_master.id)} handleChange={onHandleChange} /> <InputError message={errors.merchandise_category_id} className="mt-2" /> <InputLabel forInput="price" value="Price" /> <NumberInput type="number" name="price" value={data.price} className="m-2 input input-bordered w-full" handleChange={onHandleChange} /> <InputError message={errors.price} className="mt-2" /> <InputLabel forInput="name" value="Description" /> <TextInput type="text" name="description" value={data.description} className="m-2 input input-bordered w-full" handleChange={onHandleChange} /> <InputError message={errors.description} className="mt-2" /> <InputLabel forInput="date" value="Date" /> <DateInput type="date" name="date" value={data.date} className="m-2 input input-bordered w-full" handleChange={onHandleChange} /> <InputError message={errors.date} className="mt-2" /> <InputLabel forInput="stock" value="Stock" /> <NumberInput type="number" name="stock" value={data.stock} className="m-2 input input-bordered w-full" handleChange={onHandleChange} /> <InputError message={errors.stock} className="mt-2" /> {/* 图片字段 */} <InputLabel forInput="Image" value="Image" /> <div className="flex flex-col items-start"> <input type="file" name="image" className={`file-input file-input-bordered file-input-accent w-full max-w-xs`} onChange={(e) => setData("image", e.target.files[0])} /> </div> <InputError message={errors.image} className="mt-2" /> <PrimaryButton className="">Submit</PrimaryButton> </div> </form> </div> </AuthenticatedLayout> ); }
后端Laravel控制器代码
public function update(Request $request, Merchandise $merchandise) { $request->validate([ 'stock' => 'required|integer', 'merchandise_category_id' => 'required|integer', 'name' => 'required|string|max:255|unique:merchandises,name,'.$request->id, 'price' => 'required|integer', 'description' => 'required|string|max:255', 'date' => 'required|date' ]); if($request->file('image')){ // 删除旧文件 Storage::disk('local')->delete('public/image/'.basename($image->img)); // 添加新文件 $images = $request->file('image'); $images->storeAs('public/image', $images->hashName()); $image = Merchandise::where('id', $request->id)->update([ 'name' => $request->name, 'price' => $request->price, 'merchandise_category_id' => $request->merchandise_category_id, 'description' => $request->description, 'date' => $request->date, 'stock' => $request->stock, 'image' => $image->hashName(), 'user_id' => auth()->guard('web')->user()->id ]); if($update){ return to_route('merchandise')->with('message', 'Data Nerchandise BERHASIL diUpdate'); } return to_route('merchandise')->with('message', 'Data Nerchandise GAGAL diUpdate'); } // 无图片时的更新逻辑 $update = Merchandise::where('id', $request->id)->update([ 'name' => $request->name, 'price' => $request->price, 'merchandise_category_id' => $request->merchandise_category_id, 'description' => $request->description, 'date' => $request->date, 'stock' => $request->stock, 'user_id' => auth()->guard('web')->user()->id ]); if($update){ return to_route('merchandise')->with('message', 'Data Nerchandise BERHASIL diUpdate'); } return to_route('merchandise')->with('message', 'Data Nerchandise GAGAL diUpdate'); }
解决方案
1. 修复表单enctype
上传文件的表单必须设置enctype="multipart/form-data",否则后端无法解析混合了文件和普通字段的请求,导致$request为空。修改前端表单代码:
<form onSubmit={submit} encType="multipart/form-data">
2. 修正后端控制器的逻辑错误
控制器代码存在变量未定义、命名混淆等问题,修正后代码如下:
public function update(Request $request, Merchandise $merchandise) { $request->validate([ 'stock' => 'required|integer', 'merchandise_category_id' => 'required|integer', 'name' => 'required|string|max:255|unique:merchandises,name,'.$merchandise->id, 'price' => 'required|integer', 'description' => 'required|string|max:255', 'date' => 'required|date', 'image' => 'nullable|image|max:2048' // 新增图片验证规则,限制类型和大小 ]); // 整理基础更新数据 $updateData = [ 'name' => $request->name, 'price' => $request->price, 'merchandise_category_id' => $request->merchandise_category_id, 'description' => $request->description, 'date' => $request->date, 'stock' => $request->stock, 'user_id' => auth()->guard('web')->user()->id ]; // 处理图片上传 if($request->file('image')){ // 删除旧图片(如果存在) if($merchandise->image){ Storage::disk('local')->delete('public/image/'.$merchandise->image); } // 存储新图片并获取文件名 $imagePath = $request->file('image')->store('public/image'); $updateData['image'] = basename($imagePath); } // 使用路由模型绑定直接更新 $update = $merchandise->update($updateData); // 返回结果 $message = $update ? 'Data Merchandise BERHASIL diUpdate' : 'Data Merchandise GAGAL diUpdate'; return to_route('merchandise')->with('message', $message); }
3. 前端可选优化(图片校验)
在前端添加图片格式和大小的校验,提前拦截不符合要求的文件:
<input type="file" name="image" className={`file-input file-input-bordered file-input-accent w-full max-w-xs`} onChange={(e) => { const file = e.target.files[0]; if(!file){ setData("image", null); return; } // 限制图片类型 const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; if(!allowedTypes.includes(file.type)){ alert('仅支持JPG、PNG、GIF格式的图片'); e.target.value = ''; return; } // 限制图片大小(2MB) const maxSize = 2 * 1024 * 1024; if(file.size > maxSize){ alert('图片大小不能超过2MB'); e.target.value = ''; return; } setData("image", file); }} />
内容的提问来源于stack exchange,提问作者shinichirou ikebe
相关产品推荐
相关产品推荐

