ttlove / laravel-transactional-events
Laravel 的数据库事务感知事件调度器
Requires
- illuminate/database: ~5.8.0|^6.0|^7.0
- illuminate/events: ~5.8.0|^6.0|^7.0
- illuminate/support: ~5.8.0|^6.0|^7.0
- loophp/phptree: ^2.5
Requires (Dev)
- orchestra/testbench: ~3.8.0|~4.0|^5.0
README
此 Laravel 包引入了事务感知事件调度器。
它确保在数据库事务中派发的事件只有在外部事务成功提交时才会被派发。否则,事件将被丢弃且永远不会被派发。
目录
动机
考虑以下涉及数据库更改的订购票务的示例。orderTickets
派发了自定义的 OrderCreated
事件。然后,其监听器向用户发送包含订单详情的电子邮件。
DB::transaction(function() { ... $order = $concert->orderTickets($user, 3); // internally dispatches 'OrderCreated' event PaymentService::registerOrder($order); });
在这种情况下,由于 orderTickets
方法中的异常或甚至死锁导致事务失败,数据库更改将被完全丢弃。
不幸的是,这并不适用于已经派发的 OrderCreated
事件。这会导致即使在订单失败后,也向用户发送订单确认电子邮件。
因此,此包的目的是在数据库事务成功提交之前持有在数据库事务中派发的事件。在上面的示例中,如果事务失败,则 OrderCreated
事件永远不会被派发。
安装
Laravel
- 通过
composer
安装此包
composer require fntneves/laravel-transactional-events
- 发布提供的
transactional-events.php
配置文件
php artisan vendor:publish --provider="Neves\Events\EventServiceProvider"
Lumen
- 通过
composer
安装此包
composer require fntneves/laravel-transactional-events
- 手动将提供的
transactional-events.php
配置文件复制到config
文件夹
cp vendor/fntneves/laravel-transactional-events/src/config/transactional-events.php config/transactional-events.php
- 在
bootstrap/app.php
中注册配置文件和服务提供者
// Ensure the original EventServiceProvider is registered first, otherwise your event listeners are overriden. $app->register(App\Providers\EventServiceProvider::class); $app->configure('transactional-events'); $app->register(Neves\Events\EventServiceProvider::class);
使用方法
默认情况下,事务感知层对 App\Events
命名空间下的所有事件启用。
此包提供三种不同的方式来派发事务感知事件
- 实现
Neves\Events\Contracts\TransactionalEvent
接口; - 使用通用的
TransactionalClosureEvent
事件; - 更改配置文件。
使用接口,卢克
将事件标记为事务感知事件的最简单方法是实现 Neves\Events\Contracts\TransactionalEvent
接口
namespace App\Events; use Illuminate\Queue\SerializesModels; use Illuminate\Foundation\Events\Dispatchable; ... use Neves\Events\Contracts\TransactionalEvent; class TicketsOrdered implements TransactionalEvent { use Dispatchable, InteractsWithSockets, SerializesModels; ... }
这就完成了。无需进行其他更改。
关于作业怎么办?
此包提供了一种通用的 TransactionalClosureEvent
事件,用于将事务感知行为引入自定义行为,而无需特定的事件。
一个相关的用例是确保作业只有在事务成功提交后才被派发。
DB::transaction(function () { ... Event::dispatch(new TransactionalClosureEvent(function () { // Job will be dispatched only if the transaction commits. ProcessOrderShippingJob::dispatch($order); }); ... });
配置
配置文件包含以下参数
启用或禁用事务感知行为
'enable' => true
默认情况下,事务感知行为将应用于 App\Events
命名空间下的所有事件。
请随意使用模式和命名空间。
'transactional' => [ 'App\Events' ]
选择应该始终绕过事务感知层的事件,即由原始事件调度器处理的事件。默认情况下,所有 *ed
Eloquent 事件都被排除。此默认值的主要原因是为了避免干扰您已经存在的 Eloquent 事件监听器。
'excluded' => [ // 'eloquent.*', 'eloquent.booted', 'eloquent.retrieved', 'eloquent.saved', 'eloquent.updated', 'eloquent.created', 'eloquent.deleted', 'eloquent.restored', ],
常见问题解答
我可以用它来处理作业吗?
是的。如 使用方法 中所述,您可以使用通用的 TransactionalClosureEvent(Closure $callable)
事件来在事务提交后触发作业。
已知问题
事务感知事件在测试中不会派发。
此问题已在Laravel 5.6.16及以上版本中修复(请参阅#23832)。对于之前的版本,它与RefreshDatabase
或DatabaseTransactions
特性相关,即在每次测试后使用数据库事务重置数据库时。此包依赖于在事务开始/提交/回滚时派发的事件,并且由于每个测试都在一个在测试结束时回滚的事务中执行,因此派发的应用程序事件实际上从未被派发。为了获得预期的行为,请在您的测试中使用Neves\Testing\RefreshDatabase
或Neves\Testing\DatabaseTransactions
特性,而不是Laravel最初提供的特性。
许可证
此包是开源软件,遵循MIT许可证。