3. 控制器:创建控制器、控制器与路由绑定、控制器中间件、资源控制器、依赖注入

控制器,说白了就是路由和业务逻辑之间的「中间人」。我刚开始学 Laravel 时,总觉得把代码全写在路由文件里也挺好,直到项目越来越大,路由文件膨胀到上千行……嗯,那场面,谁看谁头疼。

今天我们就来聊聊控制器。我会把创建、绑定、中间件、资源控制器、依赖注入这几个点,一个一个拆开讲清楚。

3.1 创建控制器

Laravel 提供了 Artisan 命令行工具,创建控制器非常方便。我个人习惯用这个命令:

php artisan make:controller UserController

执行后,会在 app/Http/Controllers 目录下生成一个 UserController.php 文件。打开看看,里面是一个空的类:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    //
}

你可以在这里面写各种方法。比如:

public function index()
{
    return view('users.index');
}

public function show($id)
{
    $user = User::find($id);
    return view('users.show', compact('user'));
}
小技巧: 如果你想要同时生成模型、迁移、控制器等一整套资源,可以用 php artisan make:controller UserController --resource --model=User。我经常这么干,省事。

3.2 控制器与路由绑定

控制器写好了,怎么让路由找到它?很简单,在 routes/web.php 里绑定:

use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index']);
Route::get('/users/{id}', [UserController::class, 'show']);

这里 [UserController::class, 'index'] 的意思是:访问 /users 时,调用 UserControllerindex 方法。

你可能会问:为什么不用字符串写法了?比如 'UserController@index'

嗯,Laravel 8 之后推荐用数组写法。为什么?因为 IDE 能自动识别类名,重构时也更安全。我在项目中踩过坑——字符串写法一旦改了类名,路由就静默失效,排查起来很痛苦。

重点: 路由绑定控制器时,方法名要对应。如果方法不存在,Laravel 会报 404 错误。

3.3 控制器中间件

中间件,说白了就是请求到达控制器之前的「安检门」。比如用户登录了才能访问某些页面。

在控制器里应用中间件,有两种方式:

方式一:在路由中指定

Route::get('/users', [UserController::class, 'index'])->middleware('auth');

方式二:在控制器构造函数中指定

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('auth');
        $this->middleware('log')->only('index');
        $this->middleware('subscribed')->except('store');
    }
}

我个人更推荐第二种。为什么?因为中间件的逻辑集中在控制器里,路由文件更干净。我曾经在一个大项目中,路由文件里到处是 ->middleware(),后来维护起来简直想哭。

注意: 构造函数中的中间件,是在控制器实例化时执行的。如果你在构造函数里用了 Request 实例,要小心——此时请求还没经过中间件处理。

3.4 资源控制器

资源控制器,是 Laravel 为 CRUD 操作提供的「一键生成」方案。你想想看,一个典型的资源——比如文章、用户、产品——无非就是增删改查。Laravel 帮你把七个方法都准备好了:

HTTP 方法 URI 控制器方法 路由名称
GET /posts index() posts.index
GET /posts/create create() posts.create
POST /posts store() posts.store
GET /posts/{post} show() posts.show
GET /posts/{post}/edit edit() posts.edit
PUT/PATCH /posts/{post} update() posts.update
DELETE /posts/{post} destroy() posts.destroy

创建资源控制器:

php artisan make:controller PostController --resource

然后在路由中注册:

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

就这一行,上面七个路由全有了。是不是很爽?

不过我要提醒一句:资源控制器虽然方便,但别滥用。如果你的控制器里只有两三个方法,手动写路由反而更清晰。我见过有人把所有逻辑都塞进资源控制器,结果一个控制器里写了十几个方法,那就不叫「资源」了,叫「杂物间」。

3.5 依赖注入

依赖注入,是 Laravel 控制器的核心特性之一。说白了,就是 Laravel 的「服务容器」帮你自动把需要的对象「注入」到控制器方法里。

最常见的例子:

use Illuminate\Http\Request;

public function store(Request $request)
{
    $name = $request->input('name');
    // ...
}

这里的 Request $request 就是依赖注入。你不需要自己 new Request(),Laravel 自动帮你做了。

你还可以注入自己的服务类:

use App\Services\UserService;

public function index(UserService $userService)
{
    $users = $userService->getAllUsers();
    return view('users.index', compact('users'));
}

为什么这很重要?因为依赖注入让你的代码更容易测试和维护。你可以轻松替换掉 UserService 的实现,而不需要改控制器代码。

核心思想: 控制器不应该负责「创建」依赖,而应该「接收」依赖。这样代码更灵活,也更干净。

我记得有一次,项目里需要把用户服务从 MySQL 迁移到 Redis 缓存。因为用了依赖注入,我只需要改一下服务提供者里的绑定,控制器一行代码都没动。嗯,这就是依赖注入的魅力。

小结

这一章我们聊了控制器的五个核心点:

  • 创建控制器:用 Artisan 命令,简单高效
  • 路由绑定:用数组写法,IDE 友好
  • 中间件:在构造函数里集中管理,路由更清爽
  • 资源控制器:CRUD 一键生成,但别滥用
  • 依赖注入:让控制器更灵活、更易测试

下一章,我们会深入 Blade 模板引擎。到时候我会分享一些我在实际项目中踩过的坑——比如模板继承的陷阱、组件的正确用法。敬请期待。


公众号:蓝海资料掘金营,微信deep3321