gecche/laravel-policy-builder

构建Eloquent模型允许列表(acl)的简单方法

v11.0 2024-03-19 15:57 UTC

README

License Laravel Laravel Laravel Laravel Laravel Laravel Laravel

laravel-policy-builder

根据策略构建Eloquent模型允许列表的简单方便方式。

描述

在许多应用程序中,您使用Laravel的策略来检查用户是否有权处理资源。通常,在这些应用程序中,您还必须根据策略获取允许的资源列表。

使用此包,您将过滤资源列表的业务逻辑直接存储在策略中,并且当使用Eloquent Builder时,通过简单调用方法 acl 来获取此类列表。

文档

版本兼容性

安装

将 gecche/laravel-policy-builder 添加到 composer.json 的需求中

{
    "require": {
        "gecche/laravel-policy-builder": "11.*"
    }
}

此包使用了发现功能。

基本用法

在策略中定义构建模型允许列表的业务逻辑

假设我们有一个 Author 模型类和一个标准的 AuthorPolicy 类来定义能力方法,就像通常一样。

只需直接在 AuthorPolicy 类中添加过滤Author列表的业务逻辑。例如。

class AuthorPolicy
{
    use HandlesAuthorization;

    /**
     *
     * - All authors are allowed to users 1 and 2
     * - Only italian authors are allowed to users 3 and 4
     * - Only non-italian authors are allowed to other users
     *
     * @param   \Illuminate\Contracts\Auth\Authenticatable|null $user
     * @param  Builder $builder
     * @return mixed
     */
    public function acl($user, $builder)
    {
    
        switch ($user->getKey()) {
            case 1:
            case 2:
                return $builder;
            case 3:
            case 4:
                return $builder->where('nation','IT');
            default:
                return $builder->where('nation','<>','IT');

        }


    }

获取用户的允许模型列表

现在,要获取当前认证用户的允许作者列表,只需这样做

    Author::acl()->get();

如果您想获取用户3的列表,只需这样做

    $userForAcl = User::find(3);
    Code::acl($userForAcl)->get();

现在列表只返回意大利作者。

默认列表

让我们考虑另一个 Book 模型,对于该模型,其 BookPolicy 中没有定义 acl 方法,或者根本没有 BookPolicy

如果我们这样做

    Book::acl()->get();

我们为任何用户获取空白的模型列表。

超越基本用法

安装后,除了 acl Eloquent Builder 宏之外,该包还提供了 PolicyBuilderServiceProvider(包括 PolicyBuilder 面具),它执行将Eloquent Builder与策略链接的基础机制(通过包装Laravel的 Gate 提供程序),并提供了一些有用的方法。

基本默认构建方法: allnone

PolicyBuilder有两个公开方法,即 allnone,它们基本上分别返回所有可用的模型列表(没有任何过滤器)和空列表。

可以通过使用 setAllBuildersetNoneBuilder 方法自定义上述方法的返回值。

在以下示例中,我们使用 PolicyBuilder 的 all 方法更改了之前的 AuthorPolicy
但是,我们保留了之前的相同语义。

use Gecche\PolicyBuilder\Facades\PolicyBuilder;
use App\Models\Author;

class AuthorPolicy
{
    use HandlesAuthorization;

    /**
     *
     * - All authors are allowed to users 1 and 2
     * - Only italian authors are allowed to users 3 and 4
     * - Only non-italian authors are allowed to other users
     *
     * @param   \Illuminate\Contracts\Auth\Authenticatable|null $user
     * @param  Builder $builder
     * @return mixed
     */
    public function acl($user, $builder)
    {
    
        switch ($user->getKey()) {
            case 1:
            case 2:
                return PolicyBuilder::all($builder,Author::class);
            case 3:
            case 4:
                return $builder->where('nation','IT');
            default:
                return $builder->where('nation','<>','IT');

        }
    }

像之前一样,对于用户1和2,如果我们这样做,将返回所有作者的完整列表

    Author::acl()->get();

但是,我们可以为PolicyBuilder的 all 方法设置全局不同的语义,例如。

PolicyBuilder::setAllBuilder(function ($builder,$modelClassName = null) {
    if ($modelClassName == Author::class) {
        return $builder->where('id','<>',1);
    }
    return $builder;
});

在上面的示例中,当调用 all 方法时,作者列表中缺少id为1的作者。

同样可以与PolicyBuilder的 none 方法一起使用。

更改“上下文”

通常,用户要么可以访问某个模型,要么不能。但是,在某些“上下文”下,我们需要构建一个与标准列表不同的允许模型列表。

例如,用户可以查看图书馆中所有 Author 模型的完整列表,但不能编辑所有模型。因此,我们还想构建一个用户可以编辑的书籍列表,并且我们将改为使用具有不同业务逻辑的 editing “上下文”来构建列表。

在这种情况下,只需将“上下文”传递给构建器即可。

    //returning the 'editing' list for the authenticated user 
    Author::acl(null,'editing')->get();
    //or returning the 'editing' list for user 2 
    $userForAcl = User::find(2);
    Author::acl($userForAcl,'editing')->get();

在 AuthorPolicy 中,您必须根据之前为 acl 定义的 aclEditing 方法进行相应的定义。

beforeAcl 策略构建器和策略方法

类似于 Laravel 的 Gate before 方法,PolicyBuilder 有一个 beforeAcl 方法用于注册“beforeAcl”回调。如果注册的回调返回一个 Eloquent Builder,则不需要进一步详细说明,因此不需要任何策略。例如:

/*
 * - For user 1 (superuser) it returns the full list of models for any model and context
 * - For all the other registerd users, it returns the full list of models for Book
 */
PolicyBuilder::beforeAcl(function ($user, $modelClassName, $context, $builder) {

    if (!$user) {
        return;
    }

    if ($user->getKey() == 1 || $modelClassName == Book::class) {
        return PolicyBuilder::all($builder,$modelClassName);
    }

    return;
});  

可以在单个策略中放置一个非常相似的 beforeAcl 方法,它将在策略中定义的其他方法之前由 PolicyBuilderServiceProvider 处理。

use Gecche\PolicyBuilder\Facades\PolicyBuilder;
use App\Models\Author;

class AuthorPolicy
{
    use HandlesAuthorization;


    public function beforeAcl($user, $context, $builder) {

        if (is_null($user)) {
            return PolicyBuilder::none($builder,Author::class);
        }

        return null;

    }

    ...
    

在上面的示例中,访客用户对作者没有任何访问权限。