Laravel策略和门面怎么配合

wen PHP项目 2

本文目录导读:

Laravel策略和门面怎么配合

  1. 核心思想
  2. 创建和注册策略
  3. 使用门面调用策略
  4. 门面如何找到策略?
  5. 更高级的配合方式
  6. 完整示例
  7. 总结配合关系

在 Laravel 中,策略(Policy)门面(Gate) 是授权系统的两大核心组件,它们紧密配合,共同完成权限控制,可以理解为:

  • 策略 是具体实现授权逻辑的“操作手册”(针对某个模型)。
  • 门面 是调用授权检查的“总入口”(Gate facade 或 Auth facade)。

下面详细介绍它们的配合方式。


核心思想

  • 策略(Policy):定义某个模型(如 PostUser)的各种操作权限(如 view, create, update, delete)。
  • 门面(Gate):提供统一的接口(如 canallowsdenies)来调用这些策略,Gate 会自动根据模型类找到对应的策略。

配合流程
调用 GateGate 查找 PolicyPolicy 的方法执行检查返回 bool


创建和注册策略

1 生成策略

php artisan make:policy PostPolicy --model=Post

2 定义策略方法

// app/Policies/PostPolicy.php
class PostPolicy
{
    use HandlesAuthorization;
    public function viewAny(User $user)
    {
        return true; // 所有用户可查看列表
    }
    public function update(User $user, Post $post)
    {
        return $user->id === $post->user_id; // 只有作者可更新
    }
    public function delete(User $user, Post $post)
    {
        // 更复杂的逻辑
        return $user->id === $post->user_id || $user->isAdmin();
    }
}

3 注册策略

AuthServiceProvider$policies 属性中注册:

// app/Providers/AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        Post::class => PostPolicy::class,
    ];
    public function boot()
    {
        $this->registerPolicies();
    }
}

使用门面调用策略

有四种主要的调用方式:

1 使用 Gate Facade

use Illuminate\Support\Facades\Gate;
if (Gate::allows('update', $post)) {
    // 允许更新
}
if (Gate::denies('update', $post)) {
    // 拒绝更新
}
// 或指定用户(如管理员检查其他用户)
if (Gate::forUser($otherUser)->allows('update', $post)) { ... }

2 在 Controller 中辅助方法

public function update(Request $request, Post $post)
{
    $this->authorize('update', $post); // 自动抛出 403 异常
    // 更新逻辑...
}

3 在 Blade 视图中

@can('update', $post)
    <a href="{{ route('posts.edit', $post) }}">编辑</a>
@endcan
@cannot('delete', $post)
    <span>无权删除</span>
@endcannot

4 在模型实例上使用 can() 方法

if ($user->can('update', $post)) {
    // ...
}

门面如何找到策略?

Gate 使用约定来自动匹配:

  1. 你传入模型的类名(Post::class)或实例($post)。
  2. Gate 根据 AuthServiceProvider 中注册的 $policies 数组,找到对应的 PostPolicy
  3. 从策略中调用与方法同名的方法(如 updateupdate())。
  4. 如果没有注册策略,Gate 会尝试查找 Policy 命名规范(App\Policies\ModelNamePolicy)。

注意:如果没有显式注册,Gate 也能通过约定自动发现策略(Laravel 8+)。


更高级的配合方式

1 在 Gate 中直接定义逻辑(小型应用)

可以在 AuthServiceProvider::boot() 中直接使用 Gate::define

public function boot()
{
    $this->registerPolicies();
    // 额外定义的授权逻辑(优先级低于策略)
    Gate::define('admin-action', function ($user) {
        return $user->isAdmin();
    });
}

在 Blade 中使用:

@can('admin-action')
    <!-- 管理员专用按钮 -->
@endcan

2 使用策略的 before 方法(超级管理员绕过)

class PostPolicy
{
    public function before(User $user, $ability) // ability 表示操作名称
    {
        if ($user->isSuperAdmin()) {
            return true; // 超级管理员通过所有检查
        }
    }
    // ... 其他方法
}

注意:before 返回 true 直接通过,返回 false 会继续执行具体方法。


完整示例

场景:博客文章权限控制

策略(PostPolicy):

class PostPolicy
{
    public function view(User $user, Post $post) { ... }
    public function create(User $user) { ... }
    public function update(User $user, Post $post) { ... }
    public function delete(User $user, Post $post) { ... }
}

Controller 使用

class PostController extends Controller
{
    public function store(Request $request)
    {
        $this->authorize('create', Post::class); // 不需要模型实例
        // 创建逻辑
    }
    public function update(Request $request, Post $post)
    {
        $this->authorize('update', $post);
        // 更新逻辑
    }
}

Blade 视图

@can('update', $post)
    <form action="/posts/{{ $post->id }}" method="POST">
        @csrf @method('PUT')
        <button>更新</button>
    </form>
@endcan

API 路由(使用 Gate 门面手动检查):

Route::put('/posts/{post}', function (Post $post) {
    if (Gate::allows('update', $post)) {
        // 更新逻辑
    }
    abort(403);
});

总结配合关系

组件 职责 对应代码
Policy 定义针对某个模型的授权规则 update(User $user, Post $post)
Gate 提供统一授权检查接口 Gate::allows('update', $post)
Controller 使用 $this->authorize() 简化调用 自动调用 Gate
Blade 使用 @can 指令展示/隐藏界面元素 @can('update', $post)
User Model 使用 $user->can() 在代码中检查 $user->can('update', $post)

最佳实践

  • 模型专属权限用 Policy(代码清晰、可维护性强)。
  • 通用权限(如 admin-action)用 Gate::define 直接定义。
  • Controller 里优先使用 $this->authorize() 而非手动 Gate::allows
  • 视图层用 Blade 指令,避免在模板中写复杂 PHP 逻辑。

抱歉,评论功能暂时关闭!