直接使用 spatie/laravel-permission,避免手写角色表和中间件;中间件传参必须用数组语法如 ['role:admin'];user 模型需引入 hasroles trait;多守卫需显式设置 guardname;权限缓存需手动重置。

直接用 Spatie\laravel-permission,别自己手写角色表和中间件——90% 的权限失效、N+1 查询、缓存不生效,都源于绕过它去“造轮子”。
中间件里传参必须用数组语法
路由加中间件时写成 ->middleware('role:admin') 是无效的,Laravel 会忽略参数,等价于没传 $roles。框架只认数组形式的声明。
- ✅ 正确写法:
->middleware(['role:admin'])或->middleware(['role:admin|editor']) - ❌ 错误写法:
->middleware('role:admin')(字符串形式,参数丢失) - 中间件
handle()方法签名必须带$roles参数:public function handle($request, Closure $next, $roles),否则explode('|', $roles)拿到的是null - 如果想支持多个角色或管道分隔,
$roles接收后要手动array_map('trim', explode('|', $roles)),别依赖框架自动拆分
用户模型必须引入 HasRoles trait
光装包、跑迁移、建角色,但 User 类没加 HasRoles,所有 $user->can() 和 $user->hasRole() 都会静默返回 false。
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
- 确认
app/Models/User.php中有:use Spatie\Permission\Traits\HasRoles;和use HasRoles; - 不要只在控制器里临时
use,trait 必须挂载到模型类上才生效 - 若用了多守卫(如
admin和web),需额外调用HasRoles::setGuardName('admin'),否则默认走web - 执行过
php artisan permission:cache-reset后,首次$user->can()会触发缓存重建,别误判为逻辑错误
Gate::define() 和 Policy 方法名必须严格一致
调用 $user->can('edit-post', $post) 却在 Policy 里写了 update() 方法?那会抛 BadMethodCallException,而不是 fallback 到 Gate。
-
can('edit-post')→ 要求 Policy 里有editPost()或edit_post()方法(Laravel 自动转换驼峰/下划线) - 闭包定义
Gate::define('edit-post', function ($user, $post) { ... })时,$user必须是第一个参数,顺序错就永远拿不到当前用户 - Policy 方法第一个参数必须是类型声明的模型实例,如
public function editPost(User $user, Post $post),漏掉Post $post会导致运行时报TypeError - Spatie 包已自动注册所有数据库权限名到 Gate,所以
$user->can('delete articles')可直接用,无需再Gate::define
权限检查别在中间件里实时查库
每次请求都执行 $user->roles()->get() 或 Permission::where(...)->first(),TTFB 会从 20ms 拉到 120ms+,且极易触发 N+1。
- 复用
$request->user()?->hasRole('admin'),它走的是 Spatie 缓存好的关系,不是实时查询 - 自定义字段判断(如
account_type)直接读属性:if ($request->user()->account_type !== 'business') { abort(403); } - Blade 中用
@can('edit articles')没问题,它底层也是走缓存;但避免在循环里反复调用@can,可提前把结果存变量 - API 场景下,前端权限按钮状态应以服务端返回的布尔值为准,别靠 JS 拼字符串判断,比如把
'edit-' . $post->id当权限名传过去
最常被忽略的是:Spatie 的权限缓存默认开启,但开发时改了数据库权限却没清缓存,$user->can() 依然返回旧结果——遇到“明明刚赋了权限却不生效”,第一反应不是查代码,而是跑 php artisan permission:cache-reset。










