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

Laravel 11+Inertia CRUD问题:编辑无数据、删除失效求解决

Laravel 11 + Inertia CRUD故障排查:编辑数据不显示、删除功能失效

问题现象

使用Laravel 11结合Inertia开发CRUD功能时,出现两个异常:

  1. 点击列表页(index.tsx)的编辑按钮后,编辑页面无法加载数据库中存储的现有数据
  2. 删除按钮点击后无任何反应,且destroy和update方法无报错信息

现有代码

路由配置

Route::resource('galeri', GalleryController::class);

列表页(index.tsx)

import React from "react";
import { Link, usePage, router } from "@inertiajs/react";
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
import { PageProps } from "@/types";
import { route } from 'ziggy-js';


const createPostRoute = route('galeri.create');

// 定义单条数据类型
interface Post {
    id: number;
    nama: string;
    deskripsi: string;
    image: string;
}

// 定义数据列表结构
interface Posts {
    data: Post[];
}

const Index = ({ auth }: PageProps) => {
    const { posts } = usePage<{ posts: Posts; auth: PageProps["auth"] }>().props;
    const data: Post[] = posts.data; 

    console.log(data); 

    // 删除操作处理函数
    const handleDelete = (id: number) => {
        if (confirm("Are you sure you want to delete this post?")) {
            router.delete(route("galeri.destroy", id));
        }
    };
    

    return (
        <AuthenticatedLayout
            header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Dashboard</h2>}
        >
            <div className="container mx-auto max-w-7xl mt-4">
                <h1 className="mb-8 text-4xl font-bold text-center">Posts Index</h1>
                <div className="flex items-center justify-between mb-6">
                    <Link
                        className="px-3 py-1.5 text-white bg-blue-500 rounded-md focus:outline-none"
                        href={route("galeri.create")}
                    >
                        Create Post
                    </Link>
                </div>

                <div className="overflow-x-auto">
                    <table className="min-w-full bg-white">
                        <thead>
                            <tr>
                                <th className="px-4 py-2 bg-gray-200 text-gray-600 border-b border-gray-300 text-left text-sm uppercase font-semibold">
                                    #
                                </th>
                                <th className="px-4 py-2 bg-gray-200 text-gray-600 border-b border-gray-300 text-left text-sm uppercase font-semibold">
                                    Nama
                                </th>
                                <th className="px-4 py-2 bg-gray-200 text-gray-600 border-b border-gray-300 text-left text-sm uppercase font-semibold">
                                    deskripsi
                                </th>
                                <th className="px-4 py-2 bg-gray-200 text-gray-600 border-b border-gray-300 text-left text-sm uppercase font-semibold">
                                    image
                                </th>
                                <th className="px-4 py-2 bg-gray-200 text-gray-600 border-b border-gray-300 text-left text-sm uppercase font-semibold">
                                    Actions
                                </th>
                            </tr>
                        </thead>
                        <tbody>
                            {data && data.length > 0 ? (
                                data.map(({ id, nama, deskripsi, image }) => (
                                    <tr key={id}>
                                        <td className="px-4 py-2 border-b border-gray-300">{id}</td>
                                        <td className="px-4 py-2 border-b border-gray-300">{nama}</td>
                                        <td className="px-4 py-2 border-b border-gray-300">{deskripsi}</td>
                                        <td className="px-4 py-2 border-b border-gray-300">
                                            <img 
                                                src={`/storage/${image}`} 
                                                alt={nama} 
                                                className="h-20 w-20 object-cover rounded"
                                            />
                                        </td>
                                        <td className="px-4 py-2 border-b border-gray-300">
                                            <Link
                                                className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 rounded text-xs mr-1"
                                                href={route("galeri.edit", id)}
                                            >
                                                Edit
                                            </Link>
                                            <button
                                                className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded text-xs"
                                                onClick={() => handleDelete(id)}
                                            >
                                                Delete
                                            </button>
                                        </td>
                                    </tr>
                                ))
                            ) : (
                                <tr>
                                    <td className="px-4 py-2 border-b border-gray-300" colSpan={5}>
                                        No posts found.
                                    </td>
                                </tr>
                            )}
                        </tbody>
                    </table>
                </div>
            </div>
        </AuthenticatedLayout>
    );
};

export default Index;

GalleryController.php

<?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreGalleryRequest;
use App\Models\Gallery;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redirect;
use Inertia\Inertia;

class GalleryController extends Controller
{
    /**
     * 展示数据列表
     */
    public function index()
    {
        $postgaleris = Gallery::all();

        return Inertia::render('Gallery/index', [
            'auth' => [
                'user' => [
                    'name' => Auth::user()->name,
                    'email' => Auth::user()->email,
                ],
            ],
            'posts' => ['data' => $postgaleris],
        ]);
    }


    /**
     * 展示创建表单
     */
    public function create()
    {
        return Inertia::render('Gallery/post');
    }

    /**
     * 存储新数据
     */
    public function store(StoreGalleryRequest $request)
    {
        $data = $request->validated();

        if ($request->hasFile('image')) {
            $imagePath = $request->file('image')->store('gallery_fotos', 'public');
            $data['image'] = $imagePath;
        } else {
            $data['image'] = null;
        }

        Gallery::create($data);

        return Redirect::route('galeri.index');
    }


    /**
     * 展示单条数据
     */
    public function show(Gallery $gallery)
    {
        //
    }

    /**
     * 展示编辑表单
     */
    public function edit(Gallery $gallery)
    {
        return Inertia::render('Gallery/edit', [
            'post' => [
                'id' => $gallery->id,
                'nama' => $gallery->nama,
                'deskripsi' => $gallery->deskripsi,
                'image' => $gallery->image ? asset('storage/' . $gallery->image) : null,
            ],
        ]);
    }


    /**
     * 更新数据
     */
    public function update(StoreGalleryRequest $request, Gallery $gallery)
    {
        $data = $request->validated();

        if ($request->hasFile('image')) {
            if ($gallery->image && \Storage::disk('public')->exists($gallery->image)) {
                \Storage::disk('public')->delete($gallery->image);
            }
            $data['image'] = $request->file('image')->store('gallery_fotos', 'public');
        }

        $gallery->update($data);

        return Redirect::route('galeri.index');
    }


    /**
     * 删除数据
     */
    public function destroy(Gallery $gallery)
    {
        $gallery->delete();

        return Redirect::route('galeri.index');
    }

}

故障排查与修复方案

1. 编辑页面数据不显示问题

核心原因

编辑页面(Gallery/edit.tsx)未正确接收并使用Controller传递的post数据

修复步骤

确保edit.tsx通过usePage正确获取并渲染数据,示例代码如下:

// Gallery/edit.tsx
import React from "react";
import { usePage, router } from "@inertiajs/react";
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
import { route } from 'ziggy-js';

interface Post {
  id: number;
  nama: string;
  deskripsi: string;
  image: string | null;
}

const Edit = () => {
  const { post } = usePage<{ post: Post }>().props;

  return (
    <AuthenticatedLayout header={<h2 className="font-semibold text-xl text-gray-800 leading-tight">Edit Post</h2>}>
      <div className="container mx-auto max-w-7xl mt-4">
        <form
          onSubmit={(e) => {
            e.preventDefault();
            const formData = new FormData(e.target as HTMLFormElement);
            router.put(route("galeri.update", post.id), formData, {
              onError: (errors) => console.error('更新失败:', errors)
            });
          }}
        >
          <div className="mb-4">
            <label className="block mb-2 text-sm font-medium text-gray-700">Nama</label>
            <input
              type="text"
              name="nama"
              defaultValue={post.nama}
              className="w-full border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring-blue-500"
              required
            />
          </div>
          <div className="mb-4">
            <label className="block mb-2 text-sm font-medium text-gray-700">Deskripsi</label>
            <textarea
              name="deskripsi"
              defaultValue={post.deskripsi}
              className="w-full border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring-blue-500"
              rows={4}
              required
            ></textarea>
          </div>
          {post.image && (
            <div className="mb-4">
              <p className="mb-2 text-sm font-medium text-gray-700">当前图片</p>
              <img src={post.image} alt={post.nama} className="h-24 w-24 object-cover rounded-md" />
            </div>
          )}
          <div className="mb-6">
            <label className="block mb-2 text-sm font-medium text-gray-700">更换图片</label>
            <input
              type="file"
              name="image"
              className="w-full border-gray-300 rounded-md shadow-sm focus:border-blue-500 focus:ring-blue-500"
              accept="image/*"
            />
          </div>
          <div className="flex gap-2">
            <button 
              type="submit" 
              className="px-4 py-2 text-white bg-blue-600 rounded-md hover:bg-blue-700"
            >
              更新数据
            </button>
            <Link
              href={route("galeri.index")}
              className="px-4 py-2 text-gray-700 bg-gray-200 rounded-md hover:bg-gray-300"
            >
              返回列表
            </Link>
          </div>
        </form>
      </div>
    </AuthenticatedLayout>
  );
};

export default Edit;

2. 删除功能失效问题

核心原因

Inertia的router.delete未配置回调,且未处理可能的请求失败场景

修复步骤

修改index.tsx中的handleDelete函数,添加请求回调并确保CSRF令牌正常携带:

const handleDelete = (id: number) => {
    if (confirm("确定要删除这条数据吗?")) {
        router.delete(route("galeri.destroy", id), {
            onSuccess: () => {
                // 删除成功后刷新页面
                router.reload();
            },
            onError: (errors) => {
                console.error('删除失败:', errors);
                alert('删除失败,请稍后重试');
            }
        });
    }
};

同时确认HandleInertiaRequests中间件已共享CSRF令牌:

// app/Http/Middleware/HandleInertiaRequests.php
public function share(Request $request): array
{
    return array_merge(parent::share($request), [
        'csrf_token' => csrf_token(),
        // 其他共享数据
    ]);
}

3. 额外检查点

  • 运行php artisan route:list确认galeri.destroy和galeri.edit路由存在,参数名正确
  • 检查StoreGalleryRequest的验证规则,确保更新时允许图片字段为空:
    public function rules()
    {
        return [
            'nama' => 'required|string|max:255',
            'deskripsi' => 'required|string',
            'image' => 'nullable|image|max:2048',
        ];
    }
    
  • 打开浏览器开发者工具,查看Network标签下的DELETE/PUT请求状态,确认请求是否正常发送

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:04:59