Laravel 11 资源控制器与路由

资源控制器是 Laravel 中用于快速构建 RESTful 风格应用的工具,它将常见的 CRUD 操作(创建、读取、更新、删除)整合到一个控制器中。 配合资源路由,只需一行代码就能生成所有必要的路由,极大提高开发效率。

🎯 什么是资源控制器?

资源控制器是一个包含 indexcreatestoreshoweditupdatedestroy 七个方法的控制器, 分别对应资源的列表、创建表单、保存、详情、编辑表单、更新、删除操作。通过资源路由,Laravel 会自动将这些方法映射到相应的 URL 和 HTTP 动词。

📦 创建资源控制器

使用 Artisan 命令 make:controller 并添加 --resource 选项:

php artisan make:controller PostController --resource

生成的控制器位于 app/Http/Controllers/PostController.php,包含了上述七个方法的基本骨架:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index() {}
    public function create() {}
    public function store(Request $request) {}
    public function show($id) {}
    public function edit($id) {}
    public function update(Request $request, $id) {}
    public function destroy($id) {}
}

🔗 资源路由定义

routes/web.php 中,使用 Route::resource 方法注册资源路由:

use App\Http\Controllers\PostController;

Route::resource('posts', PostController::class);

这一行代码会生成以下七个路由:

HTTP 方法 URI 控制器方法 路由名称
GET/postsindexposts.index
GET/posts/createcreateposts.create
POST/postsstoreposts.store
GET/posts/{post}showposts.show
GET/posts/{post}/editeditposts.edit
PUT/PATCH/posts/{post}updateposts.update
DELETE/posts/{post}destroyposts.destroy
💡 提示: 路由名称遵循“资源名.动作”的命名规则,例如 posts.indexposts.show,方便在代码中使用 route() 辅助函数生成 URL。

✂️ 部分资源路由

如果不需要全部七个动作,可以使用 onlyexcept 方法指定保留或排除的动作:

// 只保留 index 和 show
Route::resource('posts', PostController::class)->only(['index', 'show']);

// 排除 create 和 edit
Route::resource('posts', PostController::class)->except(['create', 'edit']);

🔄 嵌套资源路由

当资源之间存在父子关系时(如文章下的评论),可以使用嵌套资源路由:

Route::resource('posts.comments', CommentController::class);

这会生成以下路由(以部分为例):

HTTP 方法URI控制器方法路由名称
GET/posts/{post}/commentsindexposts.comments.index
POST/posts/{post}/commentsstoreposts.comments.store
GET/posts/{post}/comments/{comment}showposts.comments.show
PUT/PATCH/posts/{post}/comments/{comment}updateposts.comments.update
DELETE/posts/{post}/comments/{comment}destroyposts.comments.destroy

对应的控制器接收两个参数:父模型和子模型。可以在控制器中利用路由模型绑定简化代码。

🏷️ 自定义资源路由名称

如果需要修改默认的路由名称,可以使用 names 方法:

Route::resource('photos', PhotoController::class)->names([
    'index'   => 'photos.all',
    'show'    => 'photos.view',
    'create'  => 'photos.make',
]);

🔧 自定义资源路由参数

默认路由参数名为资源名称的单数(如 {post}),可以通过 parameters 方法自定义:

Route::resource('users', UserController::class)->parameters([
    'users' => 'user_id', // 将 {user} 改为 {user_id}
]);

📁 资源控制器中的模型绑定

在控制器方法中,可以直接类型提示模型,Laravel 会自动解析路由参数:

use App\Models\Post;

public function show(Post $post)   // {post} 参数自动注入 Post 模型
{
    return view('posts.show', compact('post'));
}

public function update(Request $request, Post $post)
{
    $post->update($request->all());
    return redirect()->route('posts.show', $post);
}
💡 注意: 参数名必须与路由参数名一致(默认是模型名的单数)。例如路由参数 {post} 对应变量 $post

🎨 实战示例:文章管理系统

以下是一个完整的文章资源控制器及路由的使用示例:

路由定义:

Route::resource('posts', PostController::class);

控制器(部分关键方法):

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::latest()->paginate(10);
        return view('posts.index', compact('posts'));
    }

    public function create()
    {
        return view('posts.create');
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
        ]);
        Post::create($validated);
        return redirect()->route('posts.index')->with('success', '文章创建成功');
    }

    public function show(Post $post)
    {
        return view('posts.show', compact('post'));
    }

    public function edit(Post $post)
    {
        return view('posts.edit', compact('post'));
    }

    public function update(Request $request, Post $post)
    {
        $validated = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
        ]);
        $post->update($validated);
        return redirect()->route('posts.show', $post)->with('success', '文章更新成功');
    }

    public function destroy(Post $post)
    {
        $post->delete();
        return redirect()->route('posts.index')->with('success', '文章已删除');
    }
}

🧩 资源路由的其他方法

  • Route::apiResource('posts', PostController::class):创建无 createedit 路由的 API 资源路由,通常用于纯 API 应用。
  • Route::resource('posts', PostController::class)->shallow():浅层嵌套,在嵌套资源中只保留父 ID 在 URL 中,子资源的独立操作不再需要父 ID。

✅ 最佳实践

  • 命名规范: 资源控制器名称使用复数形式(如 PostsController),与资源路由名一致。
  • 验证逻辑: 对于复杂的验证,建议使用表单请求(Form Request)替代在控制器中直接验证。
  • 授权: 在资源控制器中使用 authorize 方法或策略进行权限控制。
  • 仅 API: 如果构建 API,使用 apiResource 避免生成 createedit 路由。

❓ 常见问题

可以使用 names 方法自定义名称,或者使用 name 方法在资源路由基础上添加前缀。另外,可以借助路由分组和命名空间来隔离不同模块。

可以在资源路由定义之后单独定义额外的路由,但要注意顺序,避免覆盖资源路由。例如:Route::post('/posts/{post}/publish', [PostController::class, 'publish'])->name('posts.publish');

Laravel 11 默认不再为路由自动添加控制器命名空间,因此需要使用完整类名或通过 use 导入。这也是推荐的做法,可以提高代码的 IDE 友好性。

📝 小结

资源控制器和资源路由是 Laravel 实现 RESTful 架构的利器,通过少量代码即可生成标准的 CRUD 路由,减少重复劳动。 掌握资源控制器的使用,能让你快速构建出符合规范的应用,并保持代码的清晰与一致性。 下一章我们将学习表单验证,进一步提升应用的数据安全性。