Laravel 11 控制器入门

控制器是 Laravel 应用的核心组成部分,负责接收用户请求、调用业务逻辑并返回响应。 本章将带你从零开始学习控制器的基本概念、创建方法以及与路由的协作方式。

🎯 什么是控制器?

在 Laravel 中,控制器是存放业务逻辑的类,通常位于 app/Http/Controllers 目录下。 它将路由闭包中的代码集中管理,让代码结构更清晰、易于维护和测试。

📦 创建第一个控制器

使用 Artisan 命令 make:controller 可以快速生成控制器文件:

php artisan make:controller UserController

该命令会在 app/Http/Controllers 目录下创建 UserController.php 文件,内容如下:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    //
}
💡 提示: Laravel 11 中,控制器默认不继承任何基类(除非手动添加),但通常我们仍然可以继承 Controller 基类以使用一些辅助方法(如 authorize 等)。

📌 添加控制器方法

在控制器中添加公共方法,每个方法对应一个路由的处理逻辑。例如:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    public function index()
    {
        return view('users.index', ['users' => User::all()]);
    }

    public function show($id)
    {
        return view('users.show', ['user' => User::findOrFail($id)]);
    }
}

🔗 路由绑定控制器

routes/web.php 中,将 URI 指向控制器的特定方法:

use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index']);
Route::get('/users/{id}', [UserController::class, 'show']);
使用路由命名空间(可选)

如果项目中大量使用控制器,可以通过路由分组指定命名空间:

Route::namespace('App\Http\Controllers')->group(function () {
    Route::get('/users', 'UserController@index');
    // 注意:Laravel 11 中不推荐这种写法,推荐使用 ::class 方式,更利于 IDE 支持。
});

💉 依赖注入

Laravel 的服务容器可以自动解析控制器方法中的类型提示,实现依赖注入:

use Illuminate\Http\Request;
use App\Services\UserService;

class UserController extends Controller
{
    // 注入 Request 对象
    public function store(Request $request)
    {
        // $request->all() 获取所有输入
    }

    // 注入自定义服务类
    public function index(UserService $userService)
    {
        $users = $userService->getAllUsers();
        return view('users.index', compact('users'));
    }
}
💡 注意: 依赖注入的参数必须放在路由参数之前(如果有)。例如: public function update(Request $request, $id)

🧩 单动作控制器

如果控制器只负责一个动作,可以使用 __invoke 方法创建“单动作控制器”:

php artisan make:controller ShowProfileController --invokable

生成的控制器:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ShowProfileController extends Controller
{
    public function __invoke(Request $request)
    {
        // 单个方法逻辑
        return view('profile.show');
    }
}

路由绑定方式:Route::get('/profile', ShowProfileController::class);

🧬 控制器中间件

可以在控制器构造函数中指定中间件,或直接在路由中绑定:

class UserController extends Controller
{
    public function __construct()
    {
        // 为控制器中所有方法应用中间件
        $this->middleware('auth');

        // 仅为特定方法应用中间件
        $this->middleware('admin')->only(['edit', 'update', 'destroy']);

        // 排除某些方法
        $this->middleware('log')->except(['index', 'show']);
    }
}

📁 控制器资源路由

使用 Route::resource 可以快速生成符合 RESTful 规范的路由:

Route::resource('photos', PhotoController::class);

此时控制器需要包含以下方法:index, create, store, show, edit, update, destroy

可以使用 --resource 选项生成带这些方法的控制器:

php artisan make:controller PhotoController --resource

📋 实战示例:文章管理控制器

以下是一个完整的控制器示例,展示了增删改查的基本操作:

@verbatim
<?php

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', '文章已删除!');
    }
}
⚠️ 注意: 在 Laravel 11 中,路由模型绑定(如 show(Post $post))会自动根据路由参数获取模型,如果未找到会自动返回 404。确保路由参数名称与变量名一致(如 {post})。

✅ 最佳实践

  • 单一职责: 每个控制器应该专注于处理一个资源(如用户、文章),避免过于庞大。
  • 使用依赖注入: 不要使用 app() 辅助函数,优先通过类型提示注入依赖。
  • 验证数据: 使用表单请求(Form Request)或将验证规则放在控制器方法中,保持代码清晰。
  • 返回视图或响应: 控制器应返回视图、重定向或 JSON 响应,不应包含 HTML 输出逻辑。

❓ 常见问题

需要确保路由参数名与模型变量名一致,并且使用了模型类型提示。例如路由定义为 /post/{post},控制器方法为 show(Post $post)

可以使用 response()->json() 返回 JSON 数据。如果同时需要支持 Web 和 API,通常将 API 逻辑放在单独的控制器目录(如 Api)中。

Laravel 11 默认不添加控制器命名空间前缀,因此必须使用完整的类名(App\Http\Controllers\UserController::class)或通过 use 导入。原来的 RouteServiceProvider 中不再默认设置 namespace

📝 小结

控制器是 Laravel 应用的核心组织单元。通过 Artisan 命令可以快速生成控制器,并与路由绑定处理各种请求。 掌握依赖注入、中间件、资源路由等技巧,能够让你构建出结构清晰、易于维护的应用。 下一章我们将深入学习控制器的依赖注入与请求处理。