You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免重复编写Laravel模型策略函数?支持控制器与视图权限校验

问题与解决方案

目前项目里每个模型的策略函数都要重复编写类似代码,冗余且低效;同时需要一套控制器与视图通用的权限校验方式,避免两边各写一套逻辑。

现有策略示例:

public function index(User $user)
{
    return $user->hasPermission('view-post');
}

解决步骤

1. 编写通用策略基类,告别重复策略代码

直接创建一个通用基类,用魔术方法自动处理所有模型的权限校验,无需为每个模型单独写策略类:

namespace App\Policies;

use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Database\Eloquent\Model;
use App\Models\User;

class BasePolicy
{
    use HandlesAuthorization;

    // 自动匹配所有动作的权限校验
    public function __call($method, $args)
    {
        $user = $args[0];
        $model = $args[1] ?? null;

        // 自动生成权限标识,比如Post模型的index动作对应view-post
        $modelName = strtolower(class_basename($model));
        $permissionName = match($method) {
            'index', 'view' => "view-{$modelName}",
            'create', 'store' => "create-{$modelName}",
            'edit', 'update' => "update-{$modelName}",
            'destroy' => "delete-{$modelName}",
            default => "{$method}-{$modelName}",
        };

        return $user->hasPermission($permissionName);
    }
}

然后在AuthServiceProvider中把所有需要权限控制的模型绑定到这个基类:

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Policies\BasePolicy;
use App\Models\Post;
use App\Models\Comment;

class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [
        Post::class => BasePolicy::class,
        Comment::class => BasePolicy::class,
        // 其他需要权限控制的模型都绑定到这里
    ];

    public function boot()
    {
        $this->registerPolicies();
    }
}

2. 优化User模型的hasPermission方法

原方法存在变量错误与查询冗余,优化后通过关联查询更高效:

public function hasPermission($permission)
{
    // 依赖User模型的role关联,需提前定义
    return $this->role && $this->role->permissions()
        ->where('permission', $permission)
        ->exists();
}

// User模型补充role关联
public function role()
{
    return $this->belongsTo(Role::class);
}

// Role模型补充permissions关联(需提前定义)
// public function permissions()
// {
//     return $this->belongsToMany(Permission::class, 'role_permissions');
// }

3. 控制器与视图通用的权限校验方式

控制器端使用

保持原有调用逻辑即可,无需额外修改:

public function index(User $user)
{
    // 方式一:手动判断并跳转
    if ($user->cannot('index', Post::class)) {
        return redirect('/');
    }
    // 方式二:自动抛出403异常
    // $this->authorize('index', Post::class);
}

视图端使用

直接用Blade原生的@can/@cannot指令,底层会自动调用策略校验逻辑:

@can('index', \App\Models\Post::class)
    <a href="{{ route('posts.index') }}">查看文章列表</a>
@endcan

@cannot('create', \App\Models\Post::class)
    <p>您没有创建文章的权限</p>
@endcannot

4. 可选:全局辅助函数增强灵活性

如果需要直接校验权限字符串,可在app/helpers.php中添加全局辅助函数:

function has_permission($permission)
{
    return auth()->user()?->hasPermission($permission) ?? false;
}

之后控制器与视图均可直接调用:

// 控制器中
if (!has_permission('view-post')) {
    return redirect('/');
}
<!-- 视图中 -->
@if(has_permission('create-post'))
    <button type="button">创建文章</button>
@endif

内容的提问来源于stack exchange,提问作者mmdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 20:10:27