aw-studio/laravel-states

v1.4.0 2023-09-11 13:11 UTC

README

一个用于在eloquent Models中利用有限状态模式的包。

该包将所有状态存储在数据库表中,因此可以追踪所有状态变化及其对应的时间。由于状态通过关系映射,当模型需要新的状态时,无需创建额外的迁移。

建议

尽可能使用状态!状态可以用作代替布尔值,如 active 或时间戳,如 declined_atdeleted_at

$product->state->is('active');

这样你还可以知道何时发生状态变化到active。同时,你的应用也更具可扩展性,如果需要,可以简单地添加额外的状态。

目录

设置

  1. 通过composer安装包
composer require aw-studio/laravel-states
  1. 发布所需的资源
php artisan vendor:publish --tag="states:migrations"
  1. 运行迁移
php artisan migrate

基础知识

  1. 创建状态
class BookingState extends State
{
    const PENDING = 'pending';
    const FAILED = 'failed';
    const SUCCESSFULL = 'successfull';

    const INITIAL_STATE = self::PENDING;
    const FINAL_STATES = [self::FAILED, self::SUCCESSFULL];
}
  1. 创建转换类
class BookingStateTransitions extends State
{
    const PAYMENT_PAID = 'payment_paid';
    const PAYMENT_FAILED = 'payment_failed';
}
  1. 定义允许的转换
class BookingState extends State
{
    // ...

    public static function config()
    {
        self::set(BookingStateTransition::PAYMENT_PAID)
            ->from(self::PENDING)
            ->to(self::SUCCESSFULL);
        self::set(BookingStateTransition::PAYMENT_FAILED)
            ->from(self::PENDING)
            ->to(self::FAILED);
    }
}
  1. 设置你的模型
use AwStudio\States\Contracts\Stateful;
use AwStudio\States\HasStates;

class Booking extends Model implements Stateful
{
    use HasStates;

    protected $states = [
        'state' => BookingState::class,
        'payment_state' => ...,
    ];
}

使用方法

获取当前状态

$booking->state->current(); // "pending"
(string) $booking->state; // "pending"

确定当前状态是否为给定状态

if($booking->state->is(BookingState::PENDING)) {
    //
}

确定当前状态是否为给定状态之一

$states = [
    BookingState::PENDING,
    BookingState::SUCCESSFULL
];
if($booking->state->isAnyOf($states)) {
    //
}

确定状态是否曾在任何时候为给定状态

if($booking->state->was(BookingState::PENDING)) {
    //
}

执行转换

执行状态转换

$booking->state->transition(BookingStateTransition::PAYMENT_PAID);

通过设置fail为false来防止在当前状态不允许给定转换时抛出异常

$booking->state->transition(BookingStateTransition::PAYMENT_PAID, fail: false);

存储有关转换原因的附加信息。

$booking->state->transition(BookingStateTransition::PAYMENT_PAID, reason: "Mollie API call failed.");

确定当前状态是否允许给定转换

$booking->state->can(BookingStateTransition::PAYMENT_PAID);

在事务开始时锁定当前状态以更新,以确保在事务完成之前无法通过同时请求修改状态

DB::transaction(function() {
    // Lock the current state for update:
    $booking->state->lockForUpdate();
    
    // ...
});

预加载

重新加载当前状态

$booking->state->reload();

预加载当前状态

Booking::withCurrentState();
Booking::withCurrentState('payment_state');

$booking->loadCurrentState();
$booking->loadCurrentState('payment_state');

查询方法

过滤具有或不具有当前状态的模型

Booking::whereStateIs('payment_state', PaymentState::PAID);
Booking::orWhereStateIs('payment_state', PaymentState::PAID);
Booking::whereStateIsNot('payment_state', PaymentState::PAID);
Booking::orWhereStateIsNot('payment_state', PaymentState::PAID);
Booking::whereStateWas('payment_state', PaymentState::PAID);
Booking::whereStateWasNot('payment_state', PaymentState::PAID);

接收状态变化

$booking->states()->get() // Get all states.
$booking->states('payment_state')->get() // Get all payment states.

观察者事件

在模型观察者中监听状态变化或转换

class BookingObserver
{
    public function stateSuccessfull(Booking $booking)
    {
        // Gets fired when booking state changed to successfull.
    }
    
    public function paymentStatePaid(Booking $booking)
    {
        // Gets fired when booking payment_state changed to paid.
    }
    
    public function stateTransitionPaymentPaid(Booking $booking)
    {
        // Gets fired when state transition payment_paid gets fired.
    }
}

静态方法

BookingState::whereCan(BookingStateTransition::PAYMENT_PAID); // Gets states where from where the given transition can be executed.
BookingState::canTransitionFrom('pending', 'cancel'); // Determines if the transition can be executed for the given state.