Laravel 的 Policy 类需手动注册到 AuthServiceProvider 才生效,make:policy 仅生成文件不自动绑定;调用 $this->authorize() 时须传入完整模型实例、动作名严格匹配、策略已注册,否则静默跳过。

php artisan make:policy 生成的类默认不生效,必须手动注册、传入完整模型实例、调用方式匹配,否则 $this->authorize() 会静默跳过你的策略方法。
怎么生成 Policy 类但不自动注册
Laravel 不会自动绑定策略到模型,php artisan make:policy PostPolicy --model=Post 只做三件事:创建 app/Policies/PostPolicy.php、写好空方法骨架、在控制台提示“记得去 AuthServiceProvider 注册”。它不会修改任何配置文件,也不会往 $policies 数组里加条目。
- 如果你用的是 Laravel 9+(含 10/11),推荐在
app/Providers/AuthServiceProvider.php的$policies属性中注册:App\Models\Post::class => App\Policies\PostPolicy::class - 如果模型在根命名空间(比如
App\Post),键名就得写成App\Post::class,大小写和命名空间必须完全一致 - 别依赖“类名规范”自动匹配——Laravel 从不扫描
app/Policies/目录,没注册就等于不存在
为什么 $this->authorize('update', $post) 没走你的策略方法
最常见原因是参数没传对,Gate 根本找不到可调用的方法,直接返回 false,连策略类的构造函数都不会执行。
- 传
null:比如$post = Post::find($id)但$id不存在 →$post是null→ Gate 拒绝授权,不进策略 - 传 ID 而非模型:写成
$this->authorize('update', $id)→ Gate 没法解析类型提示,跳过策略,返回false - 动作名不一致:策略里是
public function edit(User $user, Post $post),但调用写成$this->authorize('update', $post)→ Gate 默认只匹配update方法,找不到就失败 - 资源控制器误用
authorizeResource():它依赖路由参数名为{post}(小写单数),且控制器方法签名必须是function update(Request $request, Post $post);若路由是/posts/{id}或参数名是pid,模型根本不会注入,$post为null
策略方法里访问不到 $post->user 或关联字段
Policy 方法接收的是“已查出的模型实例”,它不做任何额外查询。字段为空或关系报 N+1,不是策略的问题,而是你传进去的模型本身没加载好。
- 控制器里查模型时要显式
with()或load():比如$post = Post::with('author')->findOrFail($id) - 别在策略里写
$post->user->is_admin—— 这会触发隐式查询,也违反单一职责 - 如果不确定关系是否已加载,先用
$post->relationLoaded('author')判断,避免静默查库 - 需要额外上下文(如请求参数)?把它们和模型一起传进去:
$this->authorize('update', [$post, $request->input('status')]),然后策略方法签名改为public function update(User $user, array $args)
自定义方法名(如 canPublishDraft)怎么调用
Laravel 默认只识别 view、create、update、delete 这几个标准动作名。自定义名不会被自动映射,必须显式写出全名。
- 策略里写
public function canPublishDraft(User $user, Post $post) - 控制器里必须调
$this->authorize('canPublishDraft', $post),不能简写成'publishDraft'或'publish-draft' -
Gate::define()不支持通配符,'publish*'这种写法无效 - 想复用逻辑?抽成
private方法,别指望靠命名自动调用
真正难调试的点在于:策略不生效时既不报错也不提示,只是安静地跳过。别急着改判断逻辑,先确认 Gate::inspect('update', $post) 返回的 $result->allowed 是不是 null —— 如果是,说明 Gate 根本没找到策略方法,问题出在注册或参数上。


















