如何在 laravel 中为列表或数组定义策略?
Posted
技术标签:
【中文标题】如何在 laravel 中为列表或数组定义策略?【英文标题】:How to define policy for a list or array in laravel? 【发布时间】:2019-10-03 14:46:42 【问题描述】:我有以下政策来确定用户是否能够查看合同。
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 来检查授权,但这可能会影响性能。
有更好的方法吗?
【问题讨论】:
您如何确定用户是否可以查看合同?有关系吗? 如果是普通用户,合约里面必须要有用户的id。至于管理员,它应该总是被允许的。它已经显示在我上面的代码中,它检查用户 ID 是否等于合同的客户 ID。 你有没有想过这个问题和/或我的回答有帮助吗? 【参考方案1】:我在这种情况下经常看到的设计,是通过策略检查是否允许查看查询中的所有元素。这不能很好地扩展并且不适合分页。
与其过滤掉带有策略的合约,更好的解决方案是过滤查询中已经存在的合约。这主要是因为如果您想进行分页,您希望在执行查询之前进行所有过滤,以避免出现奇怪的分页元数据。同时还必须为每个元素运行 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();
【讨论】:
所以解决方案是将您的策略逻辑复制到范围中?我不是说你错了,但应该有比这更好的方法。 但是您不能为 all() / paginate 结果定义策略。然后,您必须针对您返回的 n 个元素检查策略。此外,如果您进行分页,您将很难在不破坏标准分页方法的情况下应用您的策略。更不用说搜索,与分页和策略相结合并不一定很有趣。对于 show() rest api 方法,当然要制定策略。你有更好的建议吗,我很好奇这里:) 同意政策不应用于收藏;这就是范围的用途。应该使用策略来验证对特定模型或操作的授权。我真正关心的是 where 放置授权逻辑,以便它可以在不同的地方重用。必须在策略和范围中都写它会很糟糕。我发现的最干净/最简单的解决方案是将您的授权逻辑放入一个范围(如您所建议的那样),然后从策略中引用该范围。我发布了我的解决方案。干杯。【参考方案2】:你必须循环,一种方式到另一种方式。在您的控制器或您的策略中循环遍历 Contract
对象没有区别,但是制定了策略来检查单个资源,所以我会在您的控制器中执行此操作。
【讨论】:
【参考方案3】:我目前使用的一个解决方案是一种混合方法,您可以在一个范围内定义您的规则,然后从策略中引用该范围,从而允许您重用您的授权逻辑。
// 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 Policy
public function view(User $user, Contract $contract)
return Contract::viewable()
->where('id', $contract->id)
->exists()
;
重要的是,您应该在检索模型集合时使用 范围,而不是为集合中的每个模型运行范围查询的策略。策略应该用于单个模型实例。
Contract::viewable()->paginate(10);
// Or
Contract::viewable()->get();
但是,当您想要检查个人合同时,您可以直接使用您的保单。
$this->authorize('view', $contract);
// Or
Auth::user()->can('view', [Contract::class, $contract]);
【讨论】:
以上是关于如何在 laravel 中为列表或数组定义策略?的主要内容,如果未能解决你的问题,请参考以下文章
我如何在 laravel 中为 spatie 包(ACL 管理)中的其他角色分配用户列表权限?
如何在 CodeIgniter 中为 Laravel 中的第一个项目定义子域路由?