资源控制器用于标准CRUD,单动作控制器用于无状态单一端点;Route::resource() 404常因未手动注册路由;--invokable仅适用于真正无分支场景;隐式绑定仅限show/edit/update/destroy且参数名路由段模型类须严格匹配。

资源控制器和单动作控制器不是“选哪个更好”,而是解决两类完全不同的问题:一个是标准 CRUD 的结构化组织,一个是单一端点的语义明确封装。用错类型,后期重构成本远高于初期多敲几行路由。
Route::resource() 为什么注册后还是 404?
最常见原因是路由根本没被加载——php artisan make:controller --resource 只生成类文件,不碰 routes/web.php 或 routes/api.php。这不是遗漏,是 Laravel 的明确设计。
- 必须手动添加
use App\Http\Controllers\PostController;和Route::resource('posts', PostController::class); - Laravel 9+ 默认禁用字符串式写法(如
'PostController@index'),漏掉use会直接报Class 'PostController' not found - 运行
php artisan route:list是唯一可靠验证方式,别靠浏览器刷新猜
什么时候该用 --invokable 而不是普通控制器?
单动作控制器只适合真正“无状态、无分支、一次调用即结束”的场景,比如健康检查、Webhook 接收、跳转页。它不是“省事的简化版”,而是语义约束。
- 生成命令:
php artisan make:controller HealthCheckController --invokable - 路由写法必须是
Route::get('/health', [HealthCheckController::class]);(注意:方括号里没有方法名,Laravel 自动调__invoke) - 如果后续要加 POST 支持、或返回不同格式(JSON/HTML)、或需要中间件差异化处理,立刻重构成普通控制器——别在
__invoke里塞 if 分支
资源控制器里 $post 参数为什么是 null?
隐式模型绑定只在 show、edit、update、destroy 四个方法生效,且对参数名、路由段、模型类三者严格校验。
- 路由必须是
/posts/{post},方法签名必须是public function show(Post $post);写成show($id)或show(Post $p)都失效 -
store方法没有 URL 参数,无法触发绑定,只能从请求体取值:$request->input('user_id') - 如果改了参数别名(如
->parameters(['posts' => 'slug'])),绑定也断开,$post会变成字符串而非模型实例
only() / except() 不是删代码,是划边界
盲目用 ->only(['index', 'show']) 看似省事,实际掩盖了接口职责模糊的问题。它本质是声明“这个控制器对外只暴露这些能力”。
- API 场景通常禁用
create和edit(前端不渲染表单),用->only(['index', 'show', 'store', 'update', 'destroy']) - 后台管理可能需要全部 7 个方法,但
destroy要加软删除确认逻辑——这时别删路由,而是在方法里abort_if(!$post->trashed(), 403) - 如果只是临时关闭某个操作(比如禁止直接删除),删路由会让前端 JS 调用
DELETE直接 404,难调试;应保留路由,在方法内做业务拦截
最容易被忽略的是资源控制器的“契约感”:它强制你按 REST 约定命名方法、组织逻辑,一旦为了绕过规则在 store 里写查询、在 index 里做权限判断,就等于放弃了资源控制器存在的意义——这时候不如退回去用普通控制器,把逻辑写清楚。


















