允许根据设置的权限编辑模型以及模型在 Laravel 中的 属性 值
Allow edit of Model based on a set permission as well as Model's property value in Laravel
我有一个位置模型,它包含两个属性:ID 和名称。
为了编辑这个模型,我设置了这条路线:
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit');
我设置了非常简单的权限:在 AuthServiceProvider
中,我在 boot
方法中检查以下
Gate::before(function ($user, $permission) {
if ($user->permissions->pluck('name')->contains($permission)) {
return true;
}
});
其中 permission
是一个包含 ID 和名称的模型,通过 permission_user
table.
映射
我设置了这些权限:
- edit_los_angeles
- edit_new_york
- edit_boston
- plenty_of_other_permissions_not_related_to_location
说了这么多,我的实际问题是:
如何将这些权限绑定到编辑位置?
我面临的问题是,不允许给定用户编辑所有位置,但可能只允许编辑一个位置。只有具有 edit_los_angeles
权限的用户才可以编辑名为 Los Angeles
.
的 Location
所以我不能将其分组为一个权限,如 edit_location
并将其添加到我的路由 ->middleware('can:edit_location')
.
相反,我需要这样的东西,我想:
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_los_angeles');
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_new_york');
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_boston');
...显然这行不通。
你会用什么方法来解决这个难题? :-)
也许我做的事情完全错了,还有更好的方法 Laravel- 做这件事的方法?
非常感谢您的提前帮助!
我正在使用 Laravel 6.0 :-)
如果您有基于位置关系的需求,则需要在数据中捕获该关系。一个好的起点是添加一个特定于这些编辑权限的枢轴 table。考虑 table、location_permissions
、user_id
和 location_id
。一旦您拥有特定的用户和位置,您就可以修改或添加权限中间件来检查此 table 中的记录。
编辑:回答关于中间件实现的问题,
实施的关键可能会通过此新枢轴定义用户模型与位置的关系来解决 table。
然后我建议添加一个额外的方法,该方法使用 locations
与模型的新关系
public function canEditLocation(Location $location): bool {
return $this->locations
->where('location_id', '=', $location->id)
->count() > 0;
}
实际的中间件是这样的:
public function handle($request, Closure $next, $location)
{
if (! $request->user()->canEditLocation($location)) {
\handle failed permission as appropriate here.
}
return $next($request);
}
我的中间件参数知识是生锈的,但我相信在 https://laravel.com/docs/master/middleware#middleware-parameters
中定义的是正确的
我的工作方法有两个假设,在控制器中使用模型绑定(无论如何你都应该这样做)。其次,位置和它需要的权限之间需要有一种关系,类似于你建议的 slug。
你的控制器函数看起来像这样。添加 FormRequest 是执行此逻辑的好方法。
class LocationController {
public function edit(EditLocationRequest $request, Location $location) { // implicit model binding
...
}
}
为了方便使用,我也做一个策略
class LocationPolicy
{
public function edit(User $user, Location $location) {
return $user->permissions->pluck('name')
->contains($location->permission_slug); // assuming we have a binding
}
}
记得在AuthServiceProvider.php
中注册政策。
protected $policies = [
Location::class => LocationPolicy::class,
];
现在在您的表单请求中使用授权方法中的策略。从这里您处于请求上下文中,您可以访问 $this->user()
上的用户,并且您可以访问所有在其名称上绑定模型的模型,例如 $this->location
.
class EditLocationRequest
{
public function authorize(): bool
{
return $this->user()->can('edit', $this->location);
}
}
现在您应该只能有一个路由定义。
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit');
编辑
在没有表单请求的情况下,如果您使用特征 AuthorizesRequests,您可以执行以下操作。这将抛出 AuthorizationException 失败。
use AuthorizesRequests;
public function edit() {
$this->authorize('edit', $location);
}
我有一个位置模型,它包含两个属性:ID 和名称。
为了编辑这个模型,我设置了这条路线:
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit');
我设置了非常简单的权限:在 AuthServiceProvider
中,我在 boot
方法中检查以下
Gate::before(function ($user, $permission) {
if ($user->permissions->pluck('name')->contains($permission)) {
return true;
}
});
其中 permission
是一个包含 ID 和名称的模型,通过 permission_user
table.
我设置了这些权限:
- edit_los_angeles
- edit_new_york
- edit_boston
- plenty_of_other_permissions_not_related_to_location
说了这么多,我的实际问题是:
如何将这些权限绑定到编辑位置?
我面临的问题是,不允许给定用户编辑所有位置,但可能只允许编辑一个位置。只有具有 edit_los_angeles
权限的用户才可以编辑名为 Los Angeles
.
的 Location
所以我不能将其分组为一个权限,如 edit_location
并将其添加到我的路由 ->middleware('can:edit_location')
.
相反,我需要这样的东西,我想:
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_los_angeles');
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_new_york');
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit')->middleware('can:edit_boston');
...显然这行不通。
你会用什么方法来解决这个难题? :-)
也许我做的事情完全错了,还有更好的方法 Laravel- 做这件事的方法?
非常感谢您的提前帮助!
我正在使用 Laravel 6.0 :-)
如果您有基于位置关系的需求,则需要在数据中捕获该关系。一个好的起点是添加一个特定于这些编辑权限的枢轴 table。考虑 table、location_permissions
、user_id
和 location_id
。一旦您拥有特定的用户和位置,您就可以修改或添加权限中间件来检查此 table 中的记录。
编辑:回答关于中间件实现的问题,
实施的关键可能会通过此新枢轴定义用户模型与位置的关系来解决 table。
然后我建议添加一个额外的方法,该方法使用 locations
与模型的新关系
public function canEditLocation(Location $location): bool {
return $this->locations
->where('location_id', '=', $location->id)
->count() > 0;
}
实际的中间件是这样的:
public function handle($request, Closure $next, $location)
{
if (! $request->user()->canEditLocation($location)) {
\handle failed permission as appropriate here.
}
return $next($request);
}
我的中间件参数知识是生锈的,但我相信在 https://laravel.com/docs/master/middleware#middleware-parameters
中定义的是正确的我的工作方法有两个假设,在控制器中使用模型绑定(无论如何你都应该这样做)。其次,位置和它需要的权限之间需要有一种关系,类似于你建议的 slug。
你的控制器函数看起来像这样。添加 FormRequest 是执行此逻辑的好方法。
class LocationController {
public function edit(EditLocationRequest $request, Location $location) { // implicit model binding
...
}
}
为了方便使用,我也做一个策略
class LocationPolicy
{
public function edit(User $user, Location $location) {
return $user->permissions->pluck('name')
->contains($location->permission_slug); // assuming we have a binding
}
}
记得在AuthServiceProvider.php
中注册政策。
protected $policies = [
Location::class => LocationPolicy::class,
];
现在在您的表单请求中使用授权方法中的策略。从这里您处于请求上下文中,您可以访问 $this->user()
上的用户,并且您可以访问所有在其名称上绑定模型的模型,例如 $this->location
.
class EditLocationRequest
{
public function authorize(): bool
{
return $this->user()->can('edit', $this->location);
}
}
现在您应该只能有一个路由定义。
Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit');
编辑
在没有表单请求的情况下,如果您使用特征 AuthorizesRequests,您可以执行以下操作。这将抛出 AuthorizationException 失败。
use AuthorizesRequests;
public function edit() {
$this->authorize('edit', $location);
}