Laravel 的事件提供了一种简单的观察者模式实现,让你能够订阅并监听各种发生在你的应用中的事件。事件类通常存储在 app/Events 目录中,而它们的监听器则存储在 app/Listeners。如果你在应用中没有看到这些目录不必担心因为它们会在你使用 Artisan 控制台命令生成事件和监听器时为你创建。
事件是解耦应用程序各个方面的一种极佳方式,因为单个事件可以有多个互不依赖的监听器。例如,您可能希望在每次订单发货时向用户发送 Slack 通知。与其将订单处理代码与 Slack 通知代码耦合,您可以触发一个 App\Events\OrderShipped 事件,监听器可以接收并使用它来分派 Slack 通知。
快速生成事件和监听器,您可以使用 make:event 和 make:listener Artisan 命令:
php artisan make:event PodcastProcessed
php artisan make:listener SendPodcastNotification --event=PodcastProcessed为方便起见,您也可以调用 make:event 和 make:listener Artisan 命令,而无需额外参数。当您这样做时,Laravel 将自动提示您输入类名,并且,在创建监听器时,会提示您它应该监听的事件:
php artisan make:event
php artisan make:listener默认情况下,Laravel 会通过扫描你应用的 Listeners 目录来自动查找并注册你的事件监听器。 当 Laravel 发现任何以 handle 或 __invoke 开头的监听器类方法时,Laravel 会将这些方法注册为事件监听器,用于方法签名中类型提示的事件:
use App\Events\PodcastProcessed;
class SendPodcastNotification
{
/**
* Handle the event.
*/
public function handle(PodcastProcessed $event): void
{
// ...
}
}你可以使用 PHP 的联合类型监听多个事件:
/**
* Handle the event.
*/
public function handle(PodcastProcessed|PodcastPublished $event): void
{
// ...
}如果你计划将你的监听器存储在一个不同的目录或多个目录中,你可以使用应用 bootstrap/app.php 文件中的 withEvents 方法,指示 Laravel 扫描这些目录:
->withEvents(discover: [
__DIR__.'/../app/Domain/Orders/Listeners',
])您可以使用 * 字符作为通配符,在多个类似目录中扫描监听器:
->withEvents(discover: [
__DIR__.'/../app/Domain/*/Listeners',
])该 event:list 命令可用于列出在您的应用程序中注册的所有监听器:
php artisan event:list为了提升您的应用程序的速度,您应该使用 optimize 或 event:cache Artisan 命令缓存所有应用程序监听器的清单。通常,此命令应作为应用程序的 部署过程 的一部分运行。框架将使用此清单来加速事件注册过程。event:clear 命令可用于销毁事件缓存。
使用 Event 门面,你可以在应用 AppServiceProvider 的 boot 方法中手动注册事件及其对应的监听器:
use App\Domain\Orders\Events\PodcastProcessed;
use App\Domain\Orders\Listeners\SendPodcastNotification;
use Illuminate\Support\Facades\Event;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(
PodcastProcessed::class,
SendPodcastNotification::class,
);
}该 event:list 命令可用于列出在你的应用程序中注册的所有监听器:
php artisan event:list通常, 监听器被定义为类; 然而, 您也可以手动注册基于闭包的事件监听器 在 此 boot 方法 的 您的 应用程序的 AppServiceProvider:
use App\Events\PodcastProcessed;
use Illuminate\Support\Facades\Event;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(function (PodcastProcessed $event) {
// ...
});
}注册基于闭包的事件监听器时,你可以将监听器闭包包装在 Illuminate\Events\queueable 函数中以指示 Laravel 使用 队列 执行监听器:
use App\Events\PodcastProcessed;
use function Illuminate\Events\queueable;
use Illuminate\Support\Facades\Event;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::listen(queueable(function (PodcastProcessed $event) {
// ...
}));
}与队列作业类似,您可以使用 onConnection、onQueue 和 delay 方法来自定义队列监听器的执行:
Event::listen(queueable(function (PodcastProcessed $event) {
// ...
})->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10)));如果您希望处理匿名队列监听器失败的情况,您可以在定义 queueable 监听器时,为 catch 方法提供一个闭包。此闭包将接收事件实例和导致监听器失败的 Throwable 实例:
use App\Events\PodcastProcessed;
use function Illuminate\Events\queueable;
use Illuminate\Support\Facades\Event;
use Throwable;
Event::listen(queueable(function (PodcastProcessed $event) {
// ...
})->catch(function (PodcastProcessed $event, Throwable $e) {
// The queued listener failed...
}));您也可以注册监听器,使用 * 字符作为通配符参数,允许您在同一个监听器上捕获多个事件。通配符监听器接收事件名称作为其第一个参数,并接收整个事件数据数组作为其第二个参数:
Event::listen('event.*', function (string $eventName, array $data) {
// ...
});事件类本质上是一个数据容器,它包含与事件相关的信息。例如,我们假设一个App\Events\OrderShipped事件接收一个Eloquent ORM对象:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderShipped
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public Order $order,
) {}
}如您所见,此事件类不包含任何逻辑。它是一个容器,用于存储已购买的 App\Models\Order 实例。事件使用的 SerializesModels trait 会在事件对象使用 PHP 的 serialize 函数序列化时,优雅地序列化任何 Eloquent 模型,例如在使用 队列监听器 时。
接下来,让我们来看看我们的示例事件的监听器。事件监听器在它们的 handle 方法中接收事件实例。make:listener Artisan 命令,当使用 --event 选项调用时,将自动导入相应的事件类,并在 handle 方法中为事件进行类型提示。在 handle 方法中,你可以执行任何必要的动作来响应事件:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
class SendShipmentNotification
{
/**
* Create the event listener.
*/
public function __construct() {}
/**
* Handle the event.
*/
public function handle(OrderShipped $event): void
{
// Access the order using $event->order...
}
}[!NOTE]
你的事件监听器还可以在其构造函数上类型提示它们所需的任何依赖。所有事件监听器都是通过 Laravel 服务容器 解析的,因此依赖将自动注入。
有时,您可能希望停止事件向其他监听器传播。您可以通过从监听器的handle方法返回false来做到这一点。
如果你的监听器将要执行一项耗时任务,例如发送电子邮件或发起 HTTP 请求,那么队列监听器会很有益. 在使用队列监听器之前,请确保配置你的队列并在你的服务器或本地开发环境中启动一个队列工作器.
为了指定监听器应该被排队,请将 ShouldQueue 接口添加到监听器类中。由 make:listener Artisan 命令生成的监听器已经将此接口导入到当前命名空间中,因此你可以立即使用它:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
// ...
}就是这样!现在,当此监听器处理的事件被分发时,该监听器将由事件分发器使用 Laravel 的队列系统自动排队。如果没有在监听器由队列执行时抛出任何异常,该排队的任务将在处理完成后自动被删除。
如果您想自定义事件监听器的队列连接、队列名称或队列延迟时间,您可以在您的监听器类上定义 $connection、$queue 或 $delay 属性:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
/**
* The name of the connection the job should be sent to.
*
* @var string|null
*/
public $connection = 'sqs';
/**
* The name of the queue the job should be sent to.
*
* @var string|null
*/
public $queue = 'listeners';
/**
* The time (seconds) before the job should be processed.
*
* @var int
*/
public $delay = 60;
}If you want to define the listener's queue connection, queue name, or delay at runtime, you may define viaConnection, viaQueue, or withDelay methods on the listener:
如果你想在运行时定义监听器的队列连接、队列名称或延迟,你可以在监听器上定义 viaConnection、viaQueue 或 withDelay 方法:
/**
* Get the name of the listener's queue connection.
*/
public function viaConnection(): string
{
return 'sqs';
}
/**
* Get the name of the listener's queue.
*/
public function viaQueue(): string
{
return 'listeners';
}
/**
* Get the number of seconds before the job should be processed.
*/
public function withDelay(OrderShipped $event): int
{
return $event->highPriority ? 0 : 60;
}有时, 您可能需要确定监听器是否应该基于一些只在运行时可用的数据入队. 为此, 可以将一个 shouldQueue 方法添加到监听器中以确定该监听器是否应该入队. 如果 shouldQueue 方法返回 false, 该监听器将不会入队:
<?php
namespace App\Listeners;
use App\Events\OrderCreated;
use Illuminate\Contracts\Queue\ShouldQueue;
class RewardGiftCard implements ShouldQueue
{
/**
* Reward a gift card to the customer.
*/
public function handle(OrderCreated $event): void
{
// ...
}
/**
* Determine whether the listener should be queued.
*/
public function shouldQueue(OrderCreated $event): bool
{
return $event->order->subtotal >= 5000;
}
}如果需要手动访问监听器底层队列作业的 delete 和 release 方法,你可以使用 Illuminate\Queue\InteractsWithQueue 特性来完成。此特性在生成的监听器上默认导入,并提供对这些方法的访问:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification implements ShouldQueue
{
use InteractsWithQueue;
/**
* Handle the event.
*/
public function handle(OrderShipped $event): void
{
if ($condition) {
$this->release(30);
}
}
}当排队监听器在数据库事务中被分派时,它们可能会在数据库事务提交之前被队列处理。发生这种情况时,您在数据库事务期间对模型或数据库记录进行的任何更新可能尚未反映在数据库中。此外,在事务中创建的任何模型或数据库记录可能在数据库中不存在。如果您的监听器依赖于这些模型,则在处理分派排队监听器的作业时,可能会发生意外错误。
如果你的队列连接的 after_commit 配置选项被设置为 false,你仍然可以通过在监听器类上实现 ShouldQueueAfterCommit 接口,来指示某个特定的队列监听器应该在所有打开的数据库事务提交后才被调度:
<?php
namespace App\Listeners;
use Illuminate\Contracts\Queue\ShouldQueueAfterCommit;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification implements ShouldQueueAfterCommit
{
use InteractsWithQueue;
}[!注意]
要了解更多关于规避这些问题的信息,请查阅有关队列作业和数据库事务的文档。
排队监听器也可以利用作业中间件。作业中间件允许您在排队监听器执行时封装自定义逻辑,从而减少监听器本身的样板代码。创建作业中间件后,可以通过从监听器的middleware方法返回它们来将它们挂载到监听器上:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use App\Jobs\Middleware\RateLimited;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
/**
* Handle the event.
*/
public function handle(OrderShipped $event): void
{
// Process the event...
}
/**
* Get the middleware the listener should pass through.
*
* @return array<int, object>
*/
public function middleware(OrderShipped $event): array
{
return [new RateLimited];
}
}Laravel 允许你通过 加密 确保队列监听器的数据的隐私和完整性。要开始使用,只需将 ShouldBeEncrypted 接口添加到监听器类。一旦此接口被添加到该类中,Laravel 将在将你的监听器推送到队列之前自动对其进行加密:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue, ShouldBeEncrypted
{
// ...
}有时,您的排队事件监听器可能会失败。如果排队监听器超出了由您的队列工作器定义的最大尝试次数,那么您的监听器上的此 failed 方法将被调用。此 failed 方法接收事件实例和导致失败的 Throwable:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Throwable;
class SendShipmentNotification implements ShouldQueue
{
use InteractsWithQueue;
/**
* Handle the event.
*/
public function handle(OrderShipped $event): void
{
// ...
}
/**
* Handle a job failure.
*/
public function failed(OrderShipped $event, Throwable $exception): void
{
// ...
}
}如果你的某个队列监听器遇到了错误,你可能不希望它无限期地重试。因此,Laravel 提供了多种方式来指定监听器可以尝试的次数或尝试的时间长度。
你可以定义一个 tries 属性或方法在你的监听器类上,以指定在监听器被认为是失败之前,它可以被尝试的次数:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification implements ShouldQueue
{
use InteractsWithQueue;
/**
* The number of times the queued listener may be attempted.
*
* @var int
*/
public $tries = 5;
}作为定义监听器在失败前可以尝试多少次的替代方案,您可以定义监听器不应再尝试的时间点。这允许监听器在给定时间范围内被尝试任意次数。为了定义监听器不应再尝试的时间点,请在您的监听器类中添加一个 retryUntil 方法。该方法应返回一个 DateTime 实例:
use DateTime;
/**
* Determine the time at which the listener should timeout.
*/
public function retryUntil(): DateTime
{
return now()->addMinutes(5);
}如果同时 retryUntil 和 tries 都已定义,Laravel 会优先使用 retryUntil 方法。
如果您想配置 Laravel 在重试一个遇到了异常的监听器之前应该等待多少秒,您可以通过在您的监听器类上定义一个 backoff 属性来实现:
/**
* The number of seconds to wait before retrying the queued listener.
*
* @var int
*/
public $backoff = 3;如果您需要更复杂的逻辑来确定监听器的退避时间,您可以在您的监听器类上定义一个 backoff 方法:
/**
* Calculate the number of seconds to wait before retrying the queued listener.
*/
public function backoff(OrderShipped $event): int
{
return 3;
}你可以通过从 backoff 方法返回一个退避值数组,轻松配置“指数式”退避。在此示例中,第一次重试的延迟将是 1 秒,第二次重试为 5 秒,第三次重试为 10 秒,如果还有更多尝试,则每次后续重试均为 10 秒:
/**
* Calculate the number of seconds to wait before retrying the queued listener.
*
* @return list<int>
*/
public function backoff(OrderShipped $event): array
{
return [1, 5, 10];
}有时您可能希望指定一个排队监听器可以被尝试多次,但如果重试是由给定数量的未处理异常触发的,则应该失败(而不是直接通过 release 方法释放)。为此,您可以在您的监听器类上定义一个 maxExceptions 属性:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
class SendShipmentNotification implements ShouldQueue
{
use InteractsWithQueue;
/**
* The number of times the queued listener may be attempted.
*
* @var int
*/
public $tries = 25;
/**
* The maximum number of unhandled exceptions to allow before failing.
*
* @var int
*/
public $maxExceptions = 3;
/**
* Handle the event.
*/
public function handle(OrderShipped $event): void
{
// Process the event...
}
}在此示例中,侦听器将被重试最多 25 次。但是,如果侦听器抛出三个未处理的异常,它将失败。
通常,你大致知道你的队列监听器需要多长时间。因此,Laravel 允许你指定一个“超时”值。如果一个监听器处理时间超过了超时值指定的秒数,则处理该监听器的 worker 将会以错误退出。你可以通过在监听器类上定义一个 timeout 属性来设置监听器允许运行的最大秒数:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
/**
* The number of seconds the listener can run before timing out.
*
* @var int
*/
public $timeout = 120;
}如果您想表明一个监听器在超时时应被标记为失败,您可以在监听器类上定义 failOnTimeout 属性:
<?php
namespace App\Listeners;
use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;
class SendShipmentNotification implements ShouldQueue
{
/**
* Indicate if the listener should be marked as failed on timeout.
*
* @var bool
*/
public $failOnTimeout = true;
}
<?php
namespace App\Http\Controllers;
use App\Events\OrderShipped;
use App\Models\Order;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
class OrderShipmentController extends Controller
{
/**
* Ship the given order.
*/
public function store(Request $request): RedirectResponse
{
$order = Order::findOrFail($request->order_id);
// Order shipment logic...
OrderShipped::dispatch($order);
return redirect('/orders');
}
}如果您想有条件地调度一个事件,您可以使用 dispatchIf 和 dispatchUnless 方法:
OrderShipped::dispatchIf($condition, $order);
OrderShipped::dispatchUnless($condition, $order);[!注意]
在测试时,断言某些事件已被分发而无需实际触发其监听器会很有帮助。Laravel 的 内置测试辅助函数 使其变得轻而易举。
有时,你可能希望指示 Laravel 仅在活跃数据库事务提交之后分派事件。为此,你可以在事件类上实现 ShouldDispatchAfterCommit 接口。
这个接口指示 Laravel 不要分发事件,直到当前数据库事务被提交。如果事务失败,事件将被丢弃。如果在事件被分发时没有数据库事务正在进行,事件将立即被分发:
<?php
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderShipped implements ShouldDispatchAfterCommit
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*/
public function __construct(
public Order $order,
) {}
}延迟事件允许你延迟模型事件的分派和事件监听器的执行,直到特定代码块完成之后。当你需要确保在事件监听器被触发之前,所有相关记录都已被创建时,这尤其有用。
要延迟事件,提供一个闭包给 Event::defer() 方法:
use App\Models\User;
use Illuminate\Support\Facades\Event;
Event::defer(function () {
$user = User::create(['name' => 'Victoria Otwell']);
$user->posts()->create(['title' => 'My first post!']);
});闭包内触发的所有事件将在闭包执行后被派发。这确保了事件监听器能够访问在延迟执行期间创建的所有相关记录。如果闭包内发生异常,延迟事件将不会被派发。
为了仅延迟特定事件,请将一个事件数组作为第二个参数传递给 defer 方法:
use App\Models\User;
use Illuminate\Support\Facades\Event;
Event::defer(function () {
$user = User::create(['name' => 'Victoria Otwell']);
$user->posts()->create(['title' => 'My first post!']);
}, ['eloquent.created: '.User::class]);事件订阅者是可以在订阅者类本身内部订阅多个事件的类,允许您在一个类中定义多个事件处理器。订阅者应该定义一个 subscribe 方法,该方法接收一个事件调度器实例。您可以在给定的调度器上调用 listen 方法来注册事件监听器:
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;
class UserEventSubscriber
{
/**
* Handle user login events.
*/
public function handleUserLogin(Login $event): void {}
/**
* Handle user logout events.
*/
public function handleUserLogout(Logout $event): void {}
/**
* Register the listeners for the subscriber.
*/
public function subscribe(Dispatcher $events): void
{
$events->listen(
Login::class,
[UserEventSubscriber::class, 'handleUserLogin']
);
$events->listen(
Logout::class,
[UserEventSubscriber::class, 'handleUserLogout']
);
}
}如果您的事件监听器方法定义在订阅者本身中,您可能会觉得从订阅者的 subscribe 方法返回一个事件和方法名称数组更为方便。Laravel 在注册事件监听器时将自动确定订阅者的类名:
<?php
namespace App\Listeners;
use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;
class UserEventSubscriber
{
/**
* Handle user login events.
*/
public function handleUserLogin(Login $event): void {}
/**
* Handle user logout events.
*/
public function handleUserLogout(Logout $event): void {}
/**
* Register the listeners for the subscriber.
*
* @return array<string, string>
*/
public function subscribe(Dispatcher $events): array
{
return [
Login::class => 'handleUserLogin',
Logout::class => 'handleUserLogout',
];
}
}编写订阅者后,如果订阅者内的处理方法遵循 Laravel 的 [事件发现约定](#event-discovery),Laravel 将自动注册这些方法。否则,你可以使用 Event 门面的 subscribe 方法手动注册你的订阅者。通常,这应该在应用的 AppServiceProvider 的 boot 方法中完成:
<?php
namespace App\Providers;
use App\Listeners\UserEventSubscriber;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Event::subscribe(UserEventSubscriber::class);
}
}当测试分发事件的代码时,您可能希望指示 Laravel 不要实际执行事件的监听器,因为监听器的代码可以直接独立于分发相应事件的代码进行测试。当然,要测试监听器本身,您可以在您的测试中实例化一个监听器实例并直接调用 handle 方法。
<?php
use App\Events\OrderFailedToShip;
use App\Events\OrderShipped;
use Illuminate\Support\Facades\Event;
test('orders can be shipped', function () {
Event::fake();
// Perform order shipping...
// Assert that an event was dispatched...
Event::assertDispatched(OrderShipped::class);
// Assert an event was dispatched twice...
Event::assertDispatched(OrderShipped::class, 2);
// Assert an event was dispatched once...
Event::assertDispatchedOnce(OrderShipped::class);
// Assert an event was not dispatched...
Event::assertNotDispatched(OrderFailedToShip::class);
// Assert that no events were dispatched...
Event::assertNothingDispatched();
});<?php
namespace Tests\Feature;
use App\Events\OrderFailedToShip;
use App\Events\OrderShipped;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* Test order shipping.
*/
public function test_orders_can_be_shipped(): void
{
Event::fake();
// Perform order shipping...
// Assert that an event was dispatched...
Event::assertDispatched(OrderShipped::class);
// Assert an event was dispatched twice...
Event::assertDispatched(OrderShipped::class, 2);
// Assert an event was dispatched once...
Event::assertDispatchedOnce(OrderShipped::class);
// Assert an event was not dispatched...
Event::assertNotDispatched(OrderFailedToShip::class);
// Assert that no events were dispatched...
Event::assertNothingDispatched();
}
}你可以向 assertDispatched 或 assertNotDispatched 方法传递闭包,以便断言某个事件已被分发,并且该事件通过了给定的“真值测试”。如果至少有一个事件通过了给定的真值测试并被分发,那么断言将成功:
Event::assertDispatched(function (OrderShipped $event) use ($order) {
return $event->order->id === $order->id;
});如果您只是想断言一个事件监听器正在监听给定的事件,您可以使用 assertListening 方法:
Event::assertListening(
OrderShipped::class,
SendShipmentNotification::class
);
[!警告]
调用Event::fake()后,事件监听器将不会被执行。因此,如果你的测试使用了依赖事件的模型工厂,例如在模型的creating事件中创建 UUID,你应该在使用了工厂之后调用Event::fake()。
如果您只想针对特定事件集伪造事件监听器,您可以将它们传递给 fake 或 fakeFor 方法:
test('orders can be processed', function () {
Event::fake([
OrderCreated::class,
]);
$order = Order::factory()->create();
Event::assertDispatched(OrderCreated::class);
// Other events are dispatched as normal...
$order->update([
// ...
]);
});/**
* Test order process.
*/
public function test_orders_can_be_processed(): void
{
Event::fake([
OrderCreated::class,
]);
$order = Order::factory()->create();
Event::assertDispatched(OrderCreated::class);
// Other events are dispatched as normal...
$order->update([
// ...
]);
}您可以使用 except 方法伪造除了指定的一组事件之外的所有事件:
Event::fake()->except([
OrderCreated::class,
]);如果你只想在测试的某个部分中模拟事件监听器,你可以使用 fakeFor 方法:
<?php
use App\Events\OrderCreated;
use App\Models\Order;
use Illuminate\Support\Facades\Event;
test('orders can be processed', function () {
$order = Event::fakeFor(function () {
$order = Order::factory()->create();
Event::assertDispatched(OrderCreated::class);
return $order;
});
// Events are dispatched as normal and observers will run...
$order->update([
// ...
]);
});<?php
namespace Tests\Feature;
use App\Events\OrderCreated;
use App\Models\Order;
use Illuminate\Support\Facades\Event;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* Test order process.
*/
public function test_orders_can_be_processed(): void
{
$order = Event::fakeFor(function () {
$order = Order::factory()->create();
Event::assertDispatched(OrderCreated::class);
return $order;
});
// Events are dispatched as normal and observers will run...
$order->update([
// ...
]);
}
}