单动作控制器(Invokable Controller)是一种只负责处理单个请求的控制器,通过实现 __invoke() 方法来实现。
它遵循单一职责原则,让代码更加简洁、易测试,非常适合处理表单提交、API 端点等单一场景。
在 Laravel 中,普通的控制器通常包含多个方法(如 index、store、show 等),每个方法对应一个资源的不同操作。
而单动作控制器只包含一个 __invoke 方法,当控制器被当作可调用对象使用时,该方法会被自动调用。
使用 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'));
}
<?php
namespace App\Http\Controllers;
class WelcomeController extends Controller
{
public function __invoke()
{
return view('welcome');
}
}
路由:Route::get('/', WelcomeController::class);
<?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);
<?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 |
| 代码组织 | 按资源聚合相关操作 | 按职责拆分为独立类 |
| 测试难度 | 需测试多个方法 | 更聚焦,每个类只测试一个功能 |
当项目中的单动作控制器增多时,建议按照功能模块存放在子目录中,例如:
app/Http/Controllers/
├── User/
│ ├── ShowProfileController.php
│ ├── UpdateAvatarController.php
│ └── DeleteAccountController.php
├── Post/
│ ├── PublishPostController.php
│ └── ArchivePostController.php
└── WelcomeController.php
路由中引用时使用完整命名空间:Route::get('/profile', User\ShowProfileController::class);
PublishPostController、ResetPasswordController,避免使用 IndexController 这样模糊的名称。__invoke 方法中充分利用依赖注入和模型绑定,保持方法签名清晰。__invoke 方法不能定义其他公共方法。如果需要复用代码,可以提取到服务类或辅助方法中。
Controller 基类提供的辅助方法(如 authorize),可以完全不继承,直接使用一个普通类。生成的单动作控制器默认继承 Controller,但你可以根据需求移除。
单动作控制器是 Laravel 中实现单一职责的优雅方式,它让代码结构更加清晰,特别适合处理独立的功能点。 通过 Artisan 命令可以快速创建,路由绑定非常简洁,并且支持完整的依赖注入和中间件功能。 合理使用单动作控制器,能让你的 Laravel 应用更加模块化、易于维护。