bvtterfly / model-state-machine
Laravel 模型状态机
Requires
- php: ^8.1
- illuminate/contracts: ^9.0|^10.0
- spatie/laravel-package-tools: ^1.9.2
Requires (Dev)
- nunomaduro/collision: ^6.0
- nunomaduro/larastan: ^2.0.1
- orchestra/testbench: ^7.0|^8.0
- pestphp/pest: ^1.21
- pestphp/pest-plugin-laravel: ^1.1
- phpstan/extension-installer: ^1.1
- phpstan/phpstan-deprecation-rules: ^1.0
- phpstan/phpstan-phpunit: ^1.0
- phpunit/phpunit: ^9.5
This package is auto-updated.
Last update: 2024-03-05 14:47:02 UTC
README
🚨 此包已被放弃 🚨
我不再使用 Laravel,无法证明维护此包所需的时间。这就是我选择放弃它的原因。您可以随意复制我的代码并维护自己的副本。
Laravel 模型状态机
此包为 Laravel Eloquent 模型属性创建状态机提供支持。
要求
- PHP 8.1 或更高版本
- Laravel 9.x 或更高版本
安装
您可以通过 composer 安装此包。
composer require bvtterfly/model-state-machine
用法
例如,我们有一个博客系统,我们的博客文章有三个状态:草稿、待审和已发布。当我们编写文章时,它处于草稿状态。每当我们的博客文章写完时,我们将其安排在将来发布,这会改变文章的状态为待审。一旦文章发布,状态将变为已发布。
表示博客文章状态状态的简单枚举如下
use Bvtterfly\ModelStateMachine\Attributes\AllowTransitionTo; use Bvtterfly\ModelStateMachine\Attributes\InitialState; enum PostState: string { #[InitialState] #[AllowTransitionTo(self::PENDING)] case DRAFT = 'draft'; #[AllowTransitionTo(self::PUBLISHED)] case PENDING = 'pending'; case PUBLISHED = 'published'; }
以下是博客文章模型的示例
class Post extends Model { use HasStateMachine; protected $casts = [ 'status' => PostState::class ]; public function getStateMachineFields(): array { return [ 'status' ]; } }
一个模型可以有您想要的任意多个状态机字段,您需要使用 getStateMachineFields
方法将它们添加到列表中。
由于状态机从字符串支持的枚举中加载状态配置,您需要在模型中将状态机字段转换为相关联的状态枚举。
现在,您可以得到您的状态机
$stateMachine = $post->getStateMachine('status')
获取所有状态
您可以使用 getAllStates
方法,它返回所有可用状态的集合
$stateMachine->getAllStates();
获取所有允许的转换
您可以使用 getStateTransitions
方法,它返回当前/初始状态的可用转换的集合
$stateMachine->getStateTransitions();
如果状态字段为 null 并且状态配置没有初始状态(未知状态的字段),它将抛出异常。
如果您想获取某个状态的可用的转换,您可以将它传递给该方法
$stateMachine->getStateTransitions(PostState::PENDING); // or $stateMachine->getStateTransitions('pending');
使用转换
要使用转换,请在状态字段上调用 transitionTo
方法,如下所示
$stateMachine->transitionTo(PostState::PUBLISHED); // or $stateMachine->transitionTo('published');
您可以将数组作为
transitionTo
方法的第二个参数传递,以便在您的操作和转换中需要额外的数据。
状态动作
您可以为状态更改到状态时运行的动作添加动作。在上面的示例中,也许我们想在文章发布时发送推文并发送电子邮件给订阅者。
我们可以使用 #[Actions]
属性来完成此操作。以下是我们的 PostState
的样子
enum PostState: string { #[InitialState] #[AllowTransitionTo(self::PENDING)] case DRAFT = 'draft'; #[AllowTransitionTo(self::PUBLISHED)] case PENDING = 'pending'; #[Actions(SendTweetAction::class, SendEmailToSubscribers::class)] case PUBLISHED = 'published'; }
动作是实现了 Bvtterfly\ModelStateMachine\Contracts\StateMachineAction
的类
class SendTweetAction implements StateMachineAction { public function handle(Model $model, array $additionalData): void { // send tweet... } }
您的动作也可以在构造函数上类型提示它们需要的任何依赖项。所有动作都是通过 Laravel 服务容器解析的,因此依赖项将自动注入。
转换动作
除了状态动作之外,也许您只想在特定的状态转换到另一个状态时运行动作。
您可以将动作数组作为 #[AllowTransitionTo]
的第二个参数传递。
在上面的示例中,如果我们想在文章状态更改为待审时向管理员发送通知,我们的 PostState
将看起来像这样
enum PostState: string { #[InitialState] #[AllowTransitionTo(self::PENDING, [SendNotificationToAdmin::class])] case DRAFT = 'draft'; #[AllowTransitionTo(self::PUBLISHED)] case PENDING = 'pending'; #[Actions(SendTweetAction::class, SendEmailToSubscribers::class)] case PUBLISHED = 'published'; }
转换动作在状态动作之前运行
带验证的动作
在使用过渡时,您可以作为第二个参数传递额外的数据,并且这些数据将传递给所有操作。因此,在运行操作之前验证这些数据是必要的。
验证器是实现了 Bvtterfly\ModelStateMachine\Contracts\StateMachineValidation
接口的操作。
在上面的例子中,我们希望在帖子状态更改为待处理时向管理员发送通知
$stateMachine->transitionTo(PostState::PENDING, [ 'message' => '...' ]);
以下是我们的 SendNotificationToAdmin
操作的示例
class SendNotificationToAdmin implements StateMachineAction, StateMachineValidation { public function validate(Model $model, array $additionalData): void { $validator = validator($additionalData, [ 'message' => 'required', ]); if ($validator->fails()) { // throw exception } } public function handle(Model $model, array $additionalData): void { // send notification... } }
自定义过渡类
本包包含一个默认的过渡类,该类在运行状态和过渡操作后保存新状态。如果您需要做的不仅仅是改变到新状态,您可以使用过渡类。
自定义过渡是实现了 Bvtterfly\ModelStateMachine\Contracts\StateTransition
接口的类。
例如,我们希望在帖子模型中存储更改帖子状态为 pending
状态的用户 user_id
class DraftToPending implements StateTransition { public function commitTransition( BackedEnum|string $newState, Model $model, string $field, array $additionalData ): void { $model->{$field} = $newState; $model->causer = $additionalData['user_id'] $model->save(); } }
您可以将此类作为第三个参数传递给 #[AllowTransitionTo]
。
然后,我们的 PostState
将看起来像这样
enum PostState: string { #[InitialState] #[AllowTransitionTo(self::PENDING, [SendNotificationToAdmin::class], DraftToPending::class)] case DRAFT = 'draft'; #[AllowTransitionTo(self::PUBLISHED)] case PENDING = 'pending'; #[Actions(SendTweetAction::class, SendEmailToSubscribers::class)] case PUBLISHED = 'published'; }
测试
composer test
变更日志
请参阅 变更日志 了解最近更改的详细信息。
安全漏洞
请查看我们如何报告安全漏洞的 安全策略。
鸣谢
许可证
MIT许可证(MIT)。请参阅 许可证文件 了解更多信息。