Php 允许基于设置的权限以及模型编辑';s属性值(以Laravel为单位)

Php 允许基于设置的权限以及模型编辑';s属性值(以Laravel为单位),php,laravel,laravel-6,Php,Laravel,Laravel 6,我有一个位置模型,它包含两个属性:ID和Name 要编辑此模型,我设置了以下路线: Route::get('administration/location/{location}/edit', 'LocationController@edit')->name('location.edit'); 我设置了非常简单的权限:在AuthServiceProvider中,我在boot方法中检查以下内容 Gate::before(function ($user, $permission) {

我有一个位置模型,它包含两个属性:ID和Name

要编辑此模型,我设置了以下路线:

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\u user
表映射

我已设置以下权限:

  • 编辑洛杉矶
  • 编辑纽约
  • 编辑波士顿大学
  • 大量的\u其他\u权限\u与\u位置无关\u
经过这些漫无边际的讨论,我真正的问题是:

如何将这些权限绑定到编辑位置?

我面临的问题是,给定用户不允许编辑所有位置,但可能只允许编辑一个位置。只有具有编辑洛杉矶权限的用户才能编辑名为洛杉矶的位置 因此,我无法将其分组为一个权限,如
edit\u location
,并将其添加到我的路由
->中间件('can:edit\u 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 6.0:-)

如果您有基于位置关系的需求,那么您需要在数据中捕获该关系。一个很好的起点是为这些编辑权限添加特定的透视表。考虑一个表,<代码> Loosix权限> <代码>,使用<代码> USER ID 和<代码> LoopSyId < /代码>。一旦有了特定的用户和位置,就可以修改或添加权限中间件来检查此表中的记录

编辑:回答关于中间件实现的问题

实现的关键可能通过在用户模型上通过这个新的透视表定义与位置的关系来解决

然后,我建议添加一个额外的方法,该方法使用新的
位置
关系,并沿着

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);
    }

我的中间件参数知识已经过时了,但我相信这是正确的,正如在

中定义的那样。对于我的工作方法,在控制器中使用模型绑定(无论发生什么情况,都应该这样做)。其次,需要在位置和它所需的许可之间建立一种关系,类似于您建议的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,
];
现在,在表单请求中,使用authorize方法中的策略。在这里,您处于一个请求上下文中,您可以访问
$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');
编辑

没有表单请求,如果您使用trait authorizes请求,您可以执行以下操作。如果失败,将抛出AuthorizationException

use AuthorizesRequests;

public function edit() {
    $this->authorize('edit', $location);
}

我猜位置是一个模型?是的,位置是一个模型:-)我认为今晚会有一个更通用的解决方案来编写一个解决方案。感谢@mrhn期待它:-)位置和权限之间有任何绑定,或者您可以从名称派生出它们吗?添加的权限中间件是什么样子的?:-)非常感谢你!我将尝试这种方法:-)在哪里添加canEditLocation方法?对于位置模型本身?我认为直接将其添加到模型中是有意义的,是的。很高兴听到这有帮助!伟大的再次,非常感谢您的帮助:-)我知道这与您最初对盖茨的想法有点相去甚远,但我会这样做,在这里您不需要多重路线定义。哦,我刚刚从这篇文章中学到了很多!非常感谢@mrhn!我明天会试试这个。周日好好休息:-)您是否建议使用
php artisan make:request
创建表单请求以创建
EditLocationRequest
,还是将其命名为
LocationRequest
?不知道正确的惯例是什么。哇,没关系。已经有了。非常感谢您提供了这个关于如何使用策略和表单请求的好例子!谢谢!没问题,伙计,我认为这是一个非常干净的方法来实现它