mtvs/eloquent-approval

Laravel Eloquent 模型的审批流程。

v5.2.0 2023-02-18 07:13 UTC

README

Build Status

Eloquent Approval

Laravel Eloquent 模型的审批流程。

eloquent-approval-preview

为什么我们需要在应用中实现内容审批

除非你能够接受不可接受的内容、垃圾邮件以及其他可能出现在用户帖子中的违规行为,否则你需要在你的应用中加入某种形式的内容审批。

为什么采用三种状态审批流程

虽然可以使用布尔字段来批准模型,但具有三个可能值(待批准、批准和拒绝)的字段给我们提供了更多的灵活性。它区分了等待决策的模型和被拒绝的模型,并使用户清楚地了解其内容是否被拒绝。

它如何工作

设置完成后,在创建新的实体时,它们会被标记为 待批准。然后可以将其状态更改为 批准拒绝

此外,当发生更新操作并修改需要审批的属性时,实体将再次变为 挂起 状态。

默认情况下,审批作用域适用于每个查询,并过滤掉 待批准拒绝 的实体,因此只包含 批准 的实体。你可以通过显式指定来包含未被批准的实体。

安装

$ composer require mtvs/eloquent-approval

设置

注册服务提供者

默认情况下,服务提供者通过 Laravel 包发现自动注册,否则你需要在 config\app.php 中注册它。

Mtvs\EloquentApproval\ApprovalServiceProvider::class

数据库

以下方法向模式添加两个列,一个用于存储 审批状态,命名为 approval_status,另一个用于存储上次状态更新的 时间戳,命名为 approval_at

$table->approvals()

你可以更改默认的列名,但需要在模型中指定它们。

模型

Approvable 特性添加到模型中

use Illuminate\Database\Eloquent\Model;
use Mtvs\EloquentApproval\Approvable;

class Entity extends Model
{
    use Approvable;
}

如果你想要更改默认的列名,你需要通过添加类常量到你的模型中指定它们

use Illuminate\Database\Eloquent\Model;
use Mtvs\EloquentApproval\Approvable;

class Entity extends Model
{
    use Approvable;
    
    const APPROVAL_STATUS = 'custom_approval_status';
    const APPROVAL_AT = 'custom_approval_at';
}

approval_at 添加到模型的 $dates 列表中,以便在访问时获得 Carbon 实例。

需要审批的属性

当发生更新操作并修改需要审批的属性时,实体将再次变为 挂起 状态。

$entity->update($attributes); // an update with approval required modification

$entity->isPending(); // true

请注意,这仅在您对 Model 对象本身执行 更新 操作时发生,而不是使用查询 Builder 实例。

默认情况下,所有属性都需要审批。

/**
 * @return array
 */
public function approvalRequired()
{
    return ['*'];
}

/**
 * @return array
 */
public function approvalNotRequired()
{
    return [];
}

你可以覆盖它们以拥有自定义的审批所需属性集。

它们在 Eloquent 中类似于 $fillable$guardedapprovalRequired() 返回 黑名单,而 approvalNotRequired() 返回 白名单

使用方法

新创建的实体被标记为 待批准,并且默认情况下不包含在模型的查询中。

Entity::create(); // #1 pending

Entity::all(); // []

Entity::find(1); // null

包括所有实体

Entity::anyApprovalStatus()->get(); // retrieving all

Entity::anyApprovalStatus()->find(1); // retrieving one

Entity::anyApprovalStatus()->delete(); // deleting all

如果你想要在每次查询中完全禁用审批作用域,你可以在模型上设置 approvalScopeDisabled

use Illuminate\Database\Eloquent\Model;
use Mtvs\EloquentApproval\Approvable;

class Entity extends Model
{
    use Approvable;
    
    public $approvalScopeDisabled = true;
}

限制特定状态

Entity::onlyPending()->get(); // retrieving only pending entities
Entity::onlyRejected()->get(); // retrieving only rejected entities
Entity::onlyApproved()->get(); // retrieving only approved entities

更新状态

在模型对象上

你可以通过在 Model 对象上使用提供的方法来更新实体的状态。

$entity->approve(); // returns bool if the entity exists otherwise null  
$entity->reject(); // returns bool if the entity exists otherwise null  
$entity->suspend(); // returns bool if the entity exists otherwise null  

Builder 对象上

你可以通过在 Builder 对象上使用提供的方法来更新多个实体的状态。

Entity::whereIn('id', $updateIds)->approve(); // returns number of updated
Entity::whereIn('id', $updateIds)->reject(); // returns number of updated
Entity::whereIn('id', $updateIds)->suspend(); // returns number of updated

审批时间戳

当您更改实体的审批状态时,其approval_at列会更新。在实体的第一次审批操作之前,其approval_atnull

检查实体状态

您可以使用Model对象提供的提供的方法来检查实体状态。

$entity->isApproved(); // returns bool if entity exists otherwise null
$entity->isRejected(); // returns bool if entity exists otherwise null
$entity->isPending(); // returns bool if entity exists otherwise null

审批事件

有一些模型事件会在每个审批操作的之前和之后触发。

此外,还有一个名为approvalChanged的通用事件,无论实际状态如何,都会在审批状态更改时触发。

您可以通过调用与它们同名并提供回调的提供static方法来挂钩,或者通过使用具有相同名称的方法注册观察者。

use Illuminate\Database\Eloquent\Model;
use Mtvs\EloquentApproval\Approvable;

class Entity extends Model
{
    use Approvable;
    
    protected static function boot()
    {
        parent::boot();
        
        static::approving(function ($entity) {
            // You can halt the process by returning false
        });
        
        static::approved(function ($entity) {
            // $entity has been approved
        });

        // or:

        static::observe(ApprovalObserver::class);
    }
}

class ApprovalObserver
{
    public function approving($entity)
    {
        // You can halt the process by returning false
    }

    public function approved($entity)
    {
        // $entity has been approved
    }
}

Eloquent模型事件也可以映射到您应用程序的事件类。

重复审批

尝试将审批状态设置为当前值会被忽略,即:不会触发事件,审批时间戳也不会更新。在这种情况下,审批方法返回false

模型工厂

导入ApprovalFactoryStates以在使用模型工厂时使用审批状态。

    namespace Database\Factories;

    use Illuminate\Database\Eloquent\Factories\Factory;
    use Mtvs\EloquentApproval\ApprovalFactoryStates;

    class EntityFactory extends Factory
    {
        use ApprovalFactoryStates;

        public function definition()
        {
            //
        }
    }
    Entity::factory()->approved()->create();
    Entity::factory()->rejected()->create();
    Entity::factory()->suspended()->create();

处理审批HTTP请求

您可以在控制器中导入HandlesApproval以对模型执行审批操作。它包含一个抽象方法,必须实现以返回模型的类名。

    namespace App\Http\Controllers\Admin;

    use App\Http\Controllers\Controller;
    use App\Models\Entity;
    use Mtvs\EloquentApproval\HandlesApproval;

    class EntitiesController extends Controller
    {
        use HandlesApproval;

        protected function model()
        {
            return Entity::class;
        }
    }

特性中的performApproval()方法执行审批,请求应路由到该方法。它具有通过路由器传递给它的keyrequest参数。

进行路由时,不要忘记为认证和授权应用authcan中间件。

    Route::post(
        'admin/enitiy/{key}/approval', 
        'Admin\EntitiesController@performApproval'
    )->middleware(['auth', 'can:perform-approval'])

请求必须有一个approval_status键,其值为可能的值之一:approvedpendingrejected

前端组件

这里还有一些为Vue.js和Bootstrap编写的UI组件,您可以使用它们。首先使用approval:ui artisan命令安装它们,然后在您的app.js文件中注册它们。

审批按钮组件

调用<approval-buttons>并传递current-statusapproval-url属性,以便能够通过HTTP请求设置审批状态。

当发生审批操作时,它发出approval-changed事件。事件的有效负载是一个对象,包含新的approval_statusapproval_at值。使用事件来修改相应的entity上的键,这将反过来在下一个周期中更改current-status属性。

审批状态组件

调用<approval-status>并传递value属性以显示当前状态。

灵感

当我搜索eloquent模型上现有审批功能包时,我遇到了hootlex/laravel-moderation,尽管我决定编写自己的包,但我从那个包中得到了一些有用的灵感。

我还遵循了Eloquent本身编写类似部分代码的方式编写了代码的不同部分。