如何为laravel中的列表或数组定义策略?

如何为laravel中的列表或数组定义策略?,laravel,permissions,authorization,roles,laravel-passport,Laravel,Permissions,Authorization,Roles,Laravel Passport,我有以下策略来确定用户是否能够查看合同 public function view(User $user, Contract $contract) { if ($user->user_type->id == 2) { // If user is a vecino if ($user->id == $contract->customer_id) { return true;

我有以下策略来确定用户是否能够查看合同

public function view(User $user, Contract $contract)
    {
        if ($user->user_type->id == 2) { // If user is a vecino
            if ($user->id == $contract->customer_id) {
                return true;
            }
        } else if ($user->user_type->is_admin == true) { // If user is an admin
            return true;
        }

        return false;
    }
然后,使用

$this->authorize('view', $contract);
如何检查列表/数组/集合的授权?就像我通过
Contract::all()

我还没有找到任何方法来做这件事。我可以做一个循环,每次迭代调用$this->authorize来检查授权,但这可能会影响性能


有更好的方法吗?

在这种情况下,我经常看到的设计是检查是否允许通过策略查看查询中的所有元素。这不能很好地扩展,并且在分页时效果不佳

更好的解决方案不是过滤带有策略的契约,而是过滤查询中已经存在的契约。这主要是因为,如果您希望在执行查询之前进行分页,那么您希望在执行查询之前进行所有过滤,以避免出现奇怪的分页元数据。同时还必须为每个元素运行n个操作,这在1000个元素时已经是一个问题

如果执行以下查询子句,可以获得与策略相同的结果

Contract::where('user_id', $user->id)->get();
我通常做的一个版本是在用户模型中创建一个作用域,以使我自己的工作更容易

public function scopeOwned($query, User $user)
{
    return $this->query->where('user_id', $user->id);
}

Contract::owned($user)->get();

你必须循环,从一个方向到另一个方向。在控制器中的
合同
对象上循环,或在策略上循环,两者之间没有区别,但是制定策略是为了检查单个资源,所以我会在您的控制器中执行此操作。

我目前使用的一种解决方案是一种混合方法,您可以在一个范围内定义规则,然后从策略中引用该范围,从而允许您重用授权逻辑

// Contract model

public function scopeViewable($query)
{
    // If the user is admin, just return the query unfiltered.
    if (Auth::user()->user_type->is_admin) {
        return $query;
    }

    // Check the contract belongs to the logged in user. 
    return $query->where('customer_id', Auth::id());
}
然后在策略中,引用该范围,但将其限制为当前模型。确保使用
exists()
返回布尔值。这实际上是检查模型是否可见

重要的是,在检索模型集合时应该使用范围,而不是为集合中的每个模型运行范围查询的策略。策略应在单个模型实例上使用

Contract::viewable()->paginate(10);

// Or
Contract::viewable()->get();
但是,当您想要检查单个合同时,您可以直接使用您的策略

$this->authorize('view', $contract);

// Or
Auth::user()->can('view', [Contract::class, $contract]);

如何确定用户是否可以查看合同?它有关系吗?如果它是一个普通用户,合同中必须有用户的id。对于管理员,它应该总是被允许的。上面我的代码中已经显示了它,它检查用户id是否等于合同的客户id。您是否发现了这一点和/或我的回答是否有帮助?因此解决方案是将您的策略逻辑复制到范围中?我不是说你错了,但是应该有更好的方法。但是你不能为all()/paginate结果定义一个策略。然后必须根据返回的n个元素检查策略。而且,如果您进行分页,您将很难在不破坏标准分页方法的情况下应用策略。更不用说搜索了,再加上分页和策略也不好玩了。对于show()rest api方法,当然要制定策略。你有什么更好的建议吗,我很好奇:)同意政策不应该用于收集;这就是作用域的用途。应使用策略验证对特定模型或操作的授权。我真正关心的是将授权逻辑放在何处,以便它可以在不同的地方重用。如果必须在政策和范围中都写出来,那就太糟糕了。我发现的最干净/最简单的解决方案是将您的授权逻辑放入一个范围(如您所建议的),然后从策略中引用该范围。我发布了我的解决方案。干杯
$this->authorize('view', $contract);

// Or
Auth::user()->can('view', [Contract::class, $contract]);