fntneves/laravel-transactional-events

Laravel 的事务感知事件调度器

2.3.0 2024-03-11 22:15 UTC

README

Latest Stable Version Total Downloads

此 Laravel 包引入了事务感知事件调度器。
它确保在数据库事务中派发的事件只有在外部事务成功提交时才会派发。否则,事件将被丢弃且不会被派发。

注意:Laravel 8.17 引入了一个新的方法 DB::afterCommit,允许用户实现与该包相同的功能。然而,它缺乏对 Eloquent 事件的事务感知行为支持。

目录

动机

考虑以下示例,该示例涉及数据库更改的订单票务。
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\transactional 辅助函数;
  • 更改 配置文件

使用接口,路克

将事件标记为事务感知事件的最简单方法是实现 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)事件在事务提交后才触发作业。

许可证

本软件包是开源软件,采用MIT许可证