Laravel 11 单动作控制器

单动作控制器(Invokable Controller)是一种只负责处理单个请求的控制器,通过实现 __invoke() 方法来实现。 它遵循单一职责原则,让代码更加简洁、易测试,非常适合处理表单提交、API 端点等单一场景。

🎯 什么是单动作控制器?

在 Laravel 中,普通的控制器通常包含多个方法(如 indexstoreshow 等),每个方法对应一个资源的不同操作。 而单动作控制器只包含一个 __invoke 方法,当控制器被当作可调用对象使用时,该方法会被自动调用。

💡 核心思想: 一个控制器只做一件事。这符合 SOLID 原则中的单一职责原则,让每个控制器的职责更加明确。

📦 创建单动作控制器

使用 Artisan 命令 make:controller 并添加 --invokable 选项即可快速生成:

php artisan make:controller ShowProfileController --invokable

生成的控制器文件位于 app/Http/Controllers/ShowProfileController.php,内容如下:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

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

🔗 路由绑定

单动作控制器在路由中绑定非常简洁,直接使用控制器类名即可:

use App\Http\Controllers\ShowProfileController;

Route::get('/profile', ShowProfileController::class);

你也可以为单动作控制器添加中间件或命名:

Route::get('/profile', ShowProfileController::class)
    ->middleware('auth')
    ->name('profile.show');

💉 依赖注入

和普通控制器一样,单动作控制器的 __invoke 方法也支持依赖注入,可以自动解析类型提示的类:

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

public function __invoke(Request $request, UserService $userService, User $user)
{
    // $request 自动注入,$userService 通过容器解析,$user 是路由模型绑定
    $data = $userService->getProfile($user);
    return view('profile.show', compact('data'));
}

📌 实战示例

示例1:简单的欢迎页面控制器
<?php

namespace App\Http\Controllers;

class WelcomeController extends Controller
{
    public function __invoke()
    {
        return view('welcome');
    }
}

路由:Route::get('/', WelcomeController::class);

示例2:处理表单提交的单动作控制器
<?php

namespace App\Http\Controllers;

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

class SubmitContactController extends Controller
{
    public function __invoke(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|max:255',
            'email' => 'required|email',
            'message' => 'required',
        ]);

        Contact::create($validated);

        return redirect()->route('contact.success')->with('success', '感谢您的留言!');
    }
}

路由:Route::post('/contact', SubmitContactController::class);

示例3:API 端点控制器(返回 JSON)
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\JsonResponse;

class GetUserStatsController extends Controller
{
    public function __invoke(User $user): JsonResponse
    {
        return response()->json([
            'posts_count' => $user->posts()->count(),
            'followers' => $user->followers()->count(),
            'joined_at' => $user->created_at->toDateString(),
        ]);
    }
}

路由:Route::get('/users/{user}/stats', GetUserStatsController::class);

⚖️ 与普通控制器的对比

特性 普通控制器 单动作控制器
方法数量 多个(如 index, store, show 等) 仅一个 __invoke 方法
适用场景 资源管理(CRUD) 单一功能页面、表单提交、API端点、复杂操作
路由绑定 [UserController::class, 'index'] UserController::class
代码组织 按资源聚合相关操作 按职责拆分为独立类
测试难度 需测试多个方法 更聚焦,每个类只测试一个功能

🎯 适用场景

  • 静态页面或简单页面: 如关于我们、联系页面。
  • 表单提交处理: 每个表单独立处理逻辑,避免在普通控制器中堆积多个动作。
  • API 端点: 每个端点独立一个控制器,便于维护和版本管理。
  • 复杂业务逻辑: 当一个操作涉及多个服务、大量代码时,单独提取为单动作控制器更清晰。
  • 中间件分组: 可以为特定端点单独分配中间件,无需在控制器构造函数中筛选。

📁 组织单动作控制器

当项目中的单动作控制器增多时,建议按照功能模块存放在子目录中,例如:

app/Http/Controllers/
├── User/
│   ├── ShowProfileController.php
│   ├── UpdateAvatarController.php
│   └── DeleteAccountController.php
├── Post/
│   ├── PublishPostController.php
│   └── ArchivePostController.php
└── WelcomeController.php

路由中引用时使用完整命名空间:Route::get('/profile', User\ShowProfileController::class);

✅ 最佳实践与注意事项

  • 命名规范: 使用动词或业务含义命名,如 PublishPostControllerResetPasswordController,避免使用 IndexController 这样模糊的名称。
  • 避免过度拆分: 如果一组操作天然属于同一个资源且逻辑简单,仍可使用普通控制器。
  • 依赖注入与路由模型绑定:__invoke 方法中充分利用依赖注入和模型绑定,保持方法签名清晰。
  • 中间件指定: 可以在路由中直接绑定中间件,比在构造函数中指定更直观。
  • 测试友好: 单动作控制器很容易编写单元测试,因为每个类只测试一个方法。
⚠️ 注意: 单动作控制器的 __invoke 方法不能定义其他公共方法。如果需要复用代码,可以提取到服务类或辅助方法中。

❓ 常见问题

性能差异可以忽略不计。主要区别在于代码组织和可维护性,不会对应用性能产生明显影响。

技术上可以,但违背了单动作控制器的设计初衷。如果需要处理多个路由,应使用普通控制器。

不是必须的。如果你不需要 Controller 基类提供的辅助方法(如 authorize),可以完全不继承,直接使用一个普通类。生成的单动作控制器默认继承 Controller,但你可以根据需求移除。

📝 小结

单动作控制器是 Laravel 中实现单一职责的优雅方式,它让代码结构更加清晰,特别适合处理独立的功能点。 通过 Artisan 命令可以快速创建,路由绑定非常简洁,并且支持完整的依赖注入和中间件功能。 合理使用单动作控制器,能让你的 Laravel 应用更加模块化、易于维护。