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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:08:09