ttlove/laravel-transactional-events

Laravel 的数据库事务感知事件调度器

1.8.9 2020-04-15 17:03 UTC

README

Latest Stable Version TravisCI Status Scrutinizer Code Quality Total Downloads

此 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)。对于之前的版本,它与RefreshDatabaseDatabaseTransactions特性相关,即在每次测试后使用数据库事务重置数据库时。此包依赖于在事务开始/提交/回滚时派发的事件,并且由于每个测试都在一个在测试结束时回滚的事务中执行,因此派发的应用程序事件实际上从未被派发。为了获得预期的行为,请在您的测试中使用Neves\Testing\RefreshDatabaseNeves\Testing\DatabaseTransactions特性,而不是Laravel最初提供的特性。

许可证

此包是开源软件,遵循MIT许可证