跳到內容

事件

介紹

Laravel 的事件提供了一個簡單的觀察者模式實現,讓您可以訂閱和監聽應用程式內發生的各種事件。事件類別通常儲存在 app/Events 目錄中,而它們的監聽器則儲存在 app/Listeners 中。如果您在應用程式中沒有看到這些目錄,請別擔心,因為當您使用 Artisan 控制台命令生成事件和監聽器時,它們將會自動建立。

事件是解耦應用程式各個方面的絕佳方式,因為單一事件可以有多個互不依賴的監聽器。例如,您可能希望在每次訂單出貨時向您的使用者發送 Slack 通知。您可以發起一個 App\Events\OrderShipped 事件,而不是將您的訂單處理程式碼耦合到 Slack 通知程式碼,監聽器可以接收該事件並使用它來派發 Slack 通知。

生成事件和監聽器

若要快速生成事件和監聽器,您可以使用 make:eventmake:listener Artisan 命令

1php artisan make:event PodcastProcessed
2 
3php artisan make:listener SendPodcastNotification --event=PodcastProcessed

為了方便起見,您也可以在不使用額外參數的情況下調用 make:eventmake:listener Artisan 命令。當您這樣做時,Laravel 將自動提示您輸入類別名稱,以及在建立監聽器時,它應該監聽的事件

1php artisan make:event
2 
3php artisan make:listener

註冊事件和監聽器

事件探索

預設情況下,Laravel 會透過掃描應用程式的 Listeners 目錄來自動尋找和註冊您的事件監聽器。當 Laravel 找到任何以 handle__invoke 開頭的監聽器類別方法時,Laravel 會將這些方法註冊為方法簽名中類型提示的事件的事件監聽器

1use App\Events\PodcastProcessed;
2 
3class SendPodcastNotification
4{
5 /**
6 * Handle the given event.
7 */
8 public function handle(PodcastProcessed $event): void
9 {
10 // ...
11 }
12}

您可以使用 PHP 的聯合類型來監聽多個事件

1/**
2 * Handle the given event.
3 */
4public function handle(PodcastProcessed|PodcastPublished $event): void
5{
6 // ...
7}

如果您計劃將監聽器儲存在不同的目錄或多個目錄中,您可以指示 Laravel 使用應用程式 bootstrap/app.php 檔案中的 withEvents 方法來掃描這些目錄

1->withEvents(discover: [
2 __DIR__.'/../app/Domain/Orders/Listeners',
3])

您可以使用 * 字元作為萬用字元來掃描多個相似目錄中的監聽器

1->withEvents(discover: [
2 __DIR__.'/../app/Domain/*/Listeners',
3])

event:list 命令可用於列出應用程式中註冊的所有監聽器

1php artisan event:list

生產環境中的事件探索

為了提升應用程式的速度,您應該使用 optimizeevent:cache Artisan 命令快取應用程式所有監聽器的清單。通常,此命令應作為應用程式部署流程的一部分運行。此清單將被框架用於加速事件註冊過程。event:clear 命令可用於銷毀事件快取。

手動註冊事件

使用 Event facade,您可以在應用程式 AppServiceProviderboot 方法中手動註冊事件及其對應的監聽器

1use App\Domain\Orders\Events\PodcastProcessed;
2use App\Domain\Orders\Listeners\SendPodcastNotification;
3use Illuminate\Support\Facades\Event;
4 
5/**
6 * Bootstrap any application services.
7 */
8public function boot(): void
9{
10 Event::listen(
11 PodcastProcessed::class,
12 SendPodcastNotification::class,
13 );
14}

event:list 命令可用於列出應用程式中註冊的所有監聽器

1php artisan event:list

Closure 監聽器

通常,監聽器被定義為類別;但是,您也可以在應用程式 AppServiceProviderboot 方法中手動註冊基於 Closure 的事件監聽器

1use App\Events\PodcastProcessed;
2use Illuminate\Support\Facades\Event;
3 
4/**
5 * Bootstrap any application services.
6 */
7public function boot(): void
8{
9 Event::listen(function (PodcastProcessed $event) {
10 // ...
11 });
12}

可佇列的匿名事件監聽器

當註冊基於 Closure 的事件監聽器時,您可以將監聽器 Closure 包裹在 Illuminate\Events\queueable 函數中,以指示 Laravel 使用佇列執行監聽器

1use App\Events\PodcastProcessed;
2use function Illuminate\Events\queueable;
3use Illuminate\Support\Facades\Event;
4 
5/**
6 * Bootstrap any application services.
7 */
8public function boot(): void
9{
10 Event::listen(queueable(function (PodcastProcessed $event) {
11 // ...
12 }));
13}

就像佇列任務一樣,您可以使用 onConnectiononQueuedelay 方法來自訂佇列監聽器的執行

1Event::listen(queueable(function (PodcastProcessed $event) {
2 // ...
3})->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10)));

如果您想處理匿名佇列監聽器失敗,您可以在定義 queueable 監聽器時,為 catch 方法提供一個 Closure。這個 Closure 將接收事件實例和導致監聽器失敗的 Throwable 實例。

1use App\Events\PodcastProcessed;
2use function Illuminate\Events\queueable;
3use Illuminate\Support\Facades\Event;
4use Throwable;
5 
6Event::listen(queueable(function (PodcastProcessed $event) {
7 // ...
8})->catch(function (PodcastProcessed $event, Throwable $e) {
9 // The queued listener failed...
10}));

萬用字元事件監聽器

您也可以使用 * 字元作為萬用字元參數來註冊監聽器,讓您可以在同一個監聽器上捕獲多個事件。萬用字元監聽器接收事件名稱作為第一個參數,整個事件資料陣列作為第二個參數。

1Event::listen('event.*', function (string $eventName, array $data) {
2 // ...
3});

定義事件

事件類別本質上是一個資料容器,用於保存與事件相關的資訊。例如,假設 App\Events\OrderShipped 事件接收一個 Eloquent ORM 物件。

1<?php
2 
3namespace App\Events;
4 
5use App\Models\Order;
6use Illuminate\Broadcasting\InteractsWithSockets;
7use Illuminate\Foundation\Events\Dispatchable;
8use Illuminate\Queue\SerializesModels;
9 
10class OrderShipped
11{
12 use Dispatchable, InteractsWithSockets, SerializesModels;
13 
14 /**
15 * Create a new event instance.
16 */
17 public function __construct(
18 public Order $order,
19 ) {}
20}

如您所見,此事件類別不包含任何邏輯。它是已購買的 App\Models\Order 實例的容器。如果事件物件使用 PHP 的 serialize 函數序列化(例如在使用 佇列監聽器 時),事件使用的 SerializesModels trait 將優雅地序列化任何 Eloquent 模型。

定義監聽器

接下來,讓我們看看範例事件的監聽器。事件監聽器在其 handle 方法中接收事件實例。當調用 make:listener Artisan 命令並使用 --event 選項時,它會自動導入正確的事件類別,並在 handle 方法中類型提示事件。在 handle 方法中,您可以執行任何必要的操作來回應事件。

1<?php
2 
3namespace App\Listeners;
4 
5use App\Events\OrderShipped;
6 
7class SendShipmentNotification
8{
9 /**
10 * Create the event listener.
11 */
12 public function __construct() {}
13 
14 /**
15 * Handle the event.
16 */
17 public function handle(OrderShipped $event): void
18 {
19 // Access the order using $event->order...
20 }
21}

您的事件監聽器也可以在其建構子中類型提示它們需要的任何依賴項。所有事件監聽器都透過 Laravel 服務容器解析,因此依賴項將自動注入。

停止事件的傳播

有時,您可能希望停止事件向其他監聽器的傳播。您可以透過從監聽器的 handle 方法返回 false 來實現這一點。

佇列事件監聽器

如果您的監聽器將執行緩慢的任務(例如發送電子郵件或發出 HTTP 請求),則將監聽器放入佇列會很有幫助。在使用佇列監聽器之前,請確保設定您的佇列並在您的伺服器或本機開發環境中啟動佇列工作程序。

若要指定應將監聽器放入佇列,請將 ShouldQueue 介面添加到監聽器類別。由 make:listener Artisan 命令生成的監聽器已經將此介面導入到目前的命名空間中,因此您可以立即使用它。

1<?php
2 
3namespace App\Listeners;
4 
5use App\Events\OrderShipped;
6use Illuminate\Contracts\Queue\ShouldQueue;
7 
8class SendShipmentNotification implements ShouldQueue
9{
10 // ...
11}

就是這樣!現在,當派發由此監聽器處理的事件時,事件派發器將使用 Laravel 的 佇列系統自動將監聽器放入佇列。如果在佇列執行監聽器時沒有拋出例外,則佇列任務將在處理完成後自動刪除。

自訂佇列連線、名稱和延遲

如果您想自訂事件監聽器的佇列連線、佇列名稱或佇列延遲時間,您可以在監聽器類別上定義 $connection$queue$delay 屬性。

1<?php
2 
3namespace App\Listeners;
4 
5use App\Events\OrderShipped;
6use Illuminate\Contracts\Queue\ShouldQueue;
7 
8class SendShipmentNotification implements ShouldQueue
9{
10 /**
11 * The name of the connection the job should be sent to.
12 *
13 * @var string|null
14 */
15 public $connection = 'sqs';
16 
17 /**
18 * The name of the queue the job should be sent to.
19 *
20 * @var string|null
21 */
22 public $queue = 'listeners';
23 
24 /**
25 * The time (seconds) before the job should be processed.
26 *
27 * @var int
28 */
29 public $delay = 60;
30}

如果您想在運行時定義監聽器的佇列連線、佇列名稱或延遲,您可以在監聽器上定義 viaConnectionviaQueuewithDelay 方法。

1/**
2 * Get the name of the listener's queue connection.
3 */
4public function viaConnection(): string
5{
6 return 'sqs';
7}
8 
9/**
10 * Get the name of the listener's queue.
11 */
12public function viaQueue(): string
13{
14 return 'listeners';
15}
16 
17/**
18 * Get the number of seconds before the job should be processed.
19 */
20public function withDelay(OrderShipped $event): int
21{
22 return $event->highPriority ? 0 : 60;
23}

條件式佇列監聽器

有時,您可能需要根據僅在運行時可用的某些資料來確定是否應將監聽器放入佇列。為了實現這一點,可以在監聽器中新增一個 shouldQueue 方法,以確定是否應將監聽器放入佇列。如果 shouldQueue 方法返回 false,則不會將監聽器放入佇列。

1<?php
2 
3namespace App\Listeners;
4 
5use App\Events\OrderCreated;
6use Illuminate\Contracts\Queue\ShouldQueue;
7 
8class RewardGiftCard implements ShouldQueue
9{
10 /**
11 * Reward a gift card to the customer.
12 */
13 public function handle(OrderCreated $event): void
14 {
15 // ...
16 }
17 
18 /**
19 * Determine whether the listener should be queued.
20 */
21 public function shouldQueue(OrderCreated $event): bool
22 {
23 return $event->order->subtotal >= 5000;
24 }
25}

手動與佇列互動

如果您需要手動訪問監聽器底層佇列任務的 deleterelease 方法,您可以使用 Illuminate\Queue\InteractsWithQueue trait 來實現。此 trait 在生成的監聽器上預設導入,並提供對這些方法的訪問權限。

1<?php
2 
3namespace App\Listeners;
4 
5use App\Events\OrderShipped;
6use Illuminate\Contracts\Queue\ShouldQueue;
7use Illuminate\Queue\InteractsWithQueue;
8 
9class SendShipmentNotification implements ShouldQueue
10{
11 use InteractsWithQueue;
12 
13 /**
14 * Handle the event.
15 */
16 public function handle(OrderShipped $event): void
17 {
18 if (true) {
19 $this->release(30);
20 }
21 }
22}

佇列事件監聽器和資料庫事務

當在資料庫事務中派發佇列監聽器時,它們可能會在資料庫事務提交之前由佇列處理。當這種情況發生時,您在資料庫事務期間對模型或資料庫記錄所做的任何更新可能尚未反映在資料庫中。此外,事務中建立的任何模型或資料庫記錄可能不存在於資料庫中。如果您的監聽器依賴這些模型,則在處理派發佇列監聽器的任務時可能會發生意外錯誤。

如果您的佇列連線的 after_commit 設定選項設定為 false,您仍然可以透過在監聽器類別上實作 ShouldQueueAfterCommit 介面來指示特定佇列監聽器應在所有已開啟的資料庫事務提交後派發。

1<?php
2 
3namespace App\Listeners;
4 
5use Illuminate\Contracts\Queue\ShouldQueueAfterCommit;
6use Illuminate\Queue\InteractsWithQueue;
7 
8class SendShipmentNotification implements ShouldQueueAfterCommit
9{
10 use InteractsWithQueue;
11}

若要瞭解更多關於解決這些問題的方法,請參閱關於佇列任務和資料庫事務的文件。

處理失敗的任務

有時您的佇列事件監聽器可能會失敗。如果佇列監聽器超過佇列工作程序定義的最大嘗試次數,將在您的監聽器上調用 failed 方法。failed 方法接收事件實例和導致失敗的 Throwable

1<?php
2 
3namespace App\Listeners;
4 
5use App\Events\OrderShipped;
6use Illuminate\Contracts\Queue\ShouldQueue;
7use Illuminate\Queue\InteractsWithQueue;
8use Throwable;
9 
10class SendShipmentNotification implements ShouldQueue
11{
12 use InteractsWithQueue;
13 
14 /**
15 * Handle the event.
16 */
17 public function handle(OrderShipped $event): void
18 {
19 // ...
20 }
21 
22 /**
23 * Handle a job failure.
24 */
25 public function failed(OrderShipped $event, Throwable $exception): void
26 {
27 // ...
28 }
29}

指定佇列監聽器最大嘗試次數

如果您的某個佇列監聽器遇到錯誤,您可能不希望它無限期地重試。因此,Laravel 提供了多種方法來指定監聽器可以嘗試多少次或多長時間。

您可以在監聽器類別上定義 $tries 屬性,以指定在監聽器被視為失敗之前可以嘗試多少次。

1<?php
2 
3namespace App\Listeners;
4 
5use App\Events\OrderShipped;
6use Illuminate\Contracts\Queue\ShouldQueue;
7use Illuminate\Queue\InteractsWithQueue;
8 
9class SendShipmentNotification implements ShouldQueue
10{
11 use InteractsWithQueue;
12 
13 /**
14 * The number of times the queued listener may be attempted.
15 *
16 * @var int
17 */
18 public $tries = 5;
19}

作為定義監聽器在失敗之前可以嘗試多少次的替代方法,您可以定義監聽器不再嘗試的時間。這允許在給定的時間範圍內嘗試監聽器任意次數。若要定義監聽器不再嘗試的時間,請將 retryUntil 方法添加到您的監聽器類別。此方法應返回 DateTime 實例。

1use DateTime;
2 
3/**
4 * Determine the time at which the listener should timeout.
5 */
6public function retryUntil(): DateTime
7{
8 return now()->addMinutes(5);
9}

指定佇列監聽器退避策略

如果您想設定 Laravel 在重試遇到例外的監聽器之前應等待多少秒,您可以透過在監聽器類別上定義 backoff 屬性來實現。

1/**
2 * The number of seconds to wait before retrying the queued listener.
3 *
4 * @var int
5 */
6public $backoff = 3;

如果您需要更複雜的邏輯來確定監聽器的退避時間,您可以在監聽器類別上定義 backoff 方法。

1/**
2 * Calculate the number of seconds to wait before retrying the queued listener.
3 */
4public function backoff(): int
5{
6 return 3;
7}

您可以透過從 backoff 方法返回退避值陣列來輕鬆設定「指數」退避。在此範例中,重試延遲將為第一次重試 1 秒,第二次重試 5 秒,第三次重試 10 秒,如果還有更多嘗試次數,則後續每次重試均為 10 秒。

1/**
2 * Calculate the number of seconds to wait before retrying the queued listener.
3 *
4 * @return array<int, int>
5 */
6public function backoff(): array
7{
8 return [1, 5, 10];
9}

派發事件

若要派發事件,您可以調用事件上的靜態 dispatch 方法。此方法由 Illuminate\Foundation\Events\Dispatchable trait 在事件上提供。傳遞給 dispatch 方法的任何參數都將傳遞給事件的建構子。

1<?php
2 
3namespace App\Http\Controllers;
4 
5use App\Events\OrderShipped;
6use App\Http\Controllers\Controller;
7use App\Models\Order;
8use Illuminate\Http\RedirectResponse;
9use Illuminate\Http\Request;
10 
11class OrderShipmentController extends Controller
12{
13 /**
14 * Ship the given order.
15 */
16 public function store(Request $request): RedirectResponse
17 {
18 $order = Order::findOrFail($request->order_id);
19 
20 // Order shipment logic...
21 
22 OrderShipped::dispatch($order);
23 
24 return redirect('/orders');
25 }
26}

如果您想有條件地派發事件,可以使用 dispatchIfdispatchUnless 方法。

1OrderShipped::dispatchIf($condition, $order);
2 
3OrderShipped::dispatchUnless($condition, $order);

測試時,斷言已派發某些事件而不實際觸發其監聽器可能會很有幫助。Laravel 的 內建測試輔助函數 使其變得輕而易舉。

在資料庫事務後派發事件

有時,您可能希望指示 Laravel 僅在活動資料庫事務提交後才派發事件。若要執行此操作,您可以在事件類別上實作 ShouldDispatchAfterCommit 介面。

此介面指示 Laravel 在目前資料庫事務提交之前不要派發事件。如果事務失敗,事件將被丟棄。如果在派發事件時沒有進行中的資料庫事務,事件將立即派發。

1<?php
2 
3namespace App\Events;
4 
5use App\Models\Order;
6use Illuminate\Broadcasting\InteractsWithSockets;
7use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
8use Illuminate\Foundation\Events\Dispatchable;
9use Illuminate\Queue\SerializesModels;
10 
11class OrderShipped implements ShouldDispatchAfterCommit
12{
13 use Dispatchable, InteractsWithSockets, SerializesModels;
14 
15 /**
16 * Create a new event instance.
17 */
18 public function __construct(
19 public Order $order,
20 ) {}
21}

事件訂閱者

編寫事件訂閱者

事件訂閱者是可以在訂閱者類別本身中訂閱多個事件的類別,讓您可以在單一類別中定義多個事件處理程式。訂閱者應定義一個 subscribe 方法,該方法將傳遞一個事件派發器實例。您可以調用給定派發器上的 listen 方法來註冊事件監聽器。

1<?php
2 
3namespace App\Listeners;
4 
5use Illuminate\Auth\Events\Login;
6use Illuminate\Auth\Events\Logout;
7use Illuminate\Events\Dispatcher;
8 
9class UserEventSubscriber
10{
11 /**
12 * Handle user login events.
13 */
14 public function handleUserLogin(Login $event): void {}
15 
16 /**
17 * Handle user logout events.
18 */
19 public function handleUserLogout(Logout $event): void {}
20 
21 /**
22 * Register the listeners for the subscriber.
23 */
24 public function subscribe(Dispatcher $events): void
25 {
26 $events->listen(
27 Login::class,
28 [UserEventSubscriber::class, 'handleUserLogin']
29 );
30 
31 $events->listen(
32 Logout::class,
33 [UserEventSubscriber::class, 'handleUserLogout']
34 );
35 }
36}

如果您的事件監聽器方法是在訂閱者本身中定義的,您可能會發現從訂閱者的 subscribe 方法返回事件和方法名稱陣列更方便。Laravel 將在註冊事件監聽器時自動確定訂閱者的類別名稱。

1<?php
2 
3namespace App\Listeners;
4 
5use Illuminate\Auth\Events\Login;
6use Illuminate\Auth\Events\Logout;
7use Illuminate\Events\Dispatcher;
8 
9class UserEventSubscriber
10{
11 /**
12 * Handle user login events.
13 */
14 public function handleUserLogin(Login $event): void {}
15 
16 /**
17 * Handle user logout events.
18 */
19 public function handleUserLogout(Logout $event): void {}
20 
21 /**
22 * Register the listeners for the subscriber.
23 *
24 * @return array<string, string>
25 */
26 public function subscribe(Dispatcher $events): array
27 {
28 return [
29 Login::class => 'handleUserLogin',
30 Logout::class => 'handleUserLogout',
31 ];
32 }
33}

註冊事件訂閱者

在編寫訂閱者之後,如果它們遵循 Laravel 的 事件探索慣例,Laravel 將自動註冊訂閱者中的處理程式方法。否則,您可以使用 Event facade 的 subscribe 方法手動註冊您的訂閱者。通常,這應該在應用程式 AppServiceProviderboot 方法中完成。

1<?php
2 
3namespace App\Providers;
4 
5use App\Listeners\UserEventSubscriber;
6use Illuminate\Support\Facades\Event;
7use Illuminate\Support\ServiceProvider;
8 
9class AppServiceProvider extends ServiceProvider
10{
11 /**
12 * Bootstrap any application services.
13 */
14 public function boot(): void
15 {
16 Event::subscribe(UserEventSubscriber::class);
17 }
18}

測試

在測試派發事件的程式碼時,您可能希望指示 Laravel 不要實際執行事件的監聽器,因為監聽器的程式碼可以直接且獨立於派發對應事件的程式碼進行測試。當然,若要測試監聽器本身,您可以實例化監聽器實例並在測試中直接調用 handle 方法。

使用 Event facade 的 fake 方法,您可以防止監聽器執行、執行受測程式碼,然後使用 assertDispatchedassertNotDispatchedassertNothingDispatched 方法斷言應用程式派發了哪些事件。

1<?php
2 
3use App\Events\OrderFailedToShip;
4use App\Events\OrderShipped;
5use Illuminate\Support\Facades\Event;
6 
7test('orders can be shipped', function () {
8 Event::fake();
9 
10 // Perform order shipping...
11 
12 // Assert that an event was dispatched...
13 Event::assertDispatched(OrderShipped::class);
14 
15 // Assert an event was dispatched twice...
16 Event::assertDispatched(OrderShipped::class, 2);
17 
18 // Assert an event was not dispatched...
19 Event::assertNotDispatched(OrderFailedToShip::class);
20 
21 // Assert that no events were dispatched...
22 Event::assertNothingDispatched();
23});
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Events\OrderFailedToShip;
6use App\Events\OrderShipped;
7use Illuminate\Support\Facades\Event;
8use Tests\TestCase;
9 
10class ExampleTest extends TestCase
11{
12 /**
13 * Test order shipping.
14 */
15 public function test_orders_can_be_shipped(): void
16 {
17 Event::fake();
18 
19 // Perform order shipping...
20 
21 // Assert that an event was dispatched...
22 Event::assertDispatched(OrderShipped::class);
23 
24 // Assert an event was dispatched twice...
25 Event::assertDispatched(OrderShipped::class, 2);
26 
27 // Assert an event was not dispatched...
28 Event::assertNotDispatched(OrderFailedToShip::class);
29 
30 // Assert that no events were dispatched...
31 Event::assertNothingDispatched();
32 }
33}

您可以將 Closure 傳遞給 assertDispatchedassertNotDispatched 方法,以斷言已派發通過給定「真值測試」的事件。如果至少派發了一個通過給定真值測試的事件,則斷言將成功。

1Event::assertDispatched(function (OrderShipped $event) use ($order) {
2 return $event->order->id === $order->id;
3});

如果您只想斷言事件監聽器正在監聽給定事件,您可以使用 assertListening 方法。

1Event::assertListening(
2 OrderShipped::class,
3 SendShipmentNotification::class
4);

在調用 Event::fake() 之後,將不會執行任何事件監聽器。因此,如果您的測試使用依賴事件的模型工廠(例如在模型的 creating 事件期間建立 UUID),您應該在使用工廠之後調用 Event::fake()

偽造事件子集

如果您只想偽造特定事件集的事件監聽器,您可以將它們傳遞給 fakefakeFor 方法。

1test('orders can be processed', function () {
2 Event::fake([
3 OrderCreated::class,
4 ]);
5 
6 $order = Order::factory()->create();
7 
8 Event::assertDispatched(OrderCreated::class);
9 
10 // Other events are dispatched as normal...
11 $order->update([...]);
12});
1/**
2 * Test order process.
3 */
4public function test_orders_can_be_processed(): void
5{
6 Event::fake([
7 OrderCreated::class,
8 ]);
9 
10 $order = Order::factory()->create();
11 
12 Event::assertDispatched(OrderCreated::class);
13 
14 // Other events are dispatched as normal...
15 $order->update([...]);
16}

您可以使用 except 方法偽造除一組指定事件之外的所有事件。

1Event::fake()->except([
2 OrderCreated::class,
3]);

作用域事件偽造

如果您只想為測試的一部分偽造事件監聽器,您可以使用 fakeFor 方法。

1<?php
2 
3use App\Events\OrderCreated;
4use App\Models\Order;
5use Illuminate\Support\Facades\Event;
6 
7test('orders can be processed', function () {
8 $order = Event::fakeFor(function () {
9 $order = Order::factory()->create();
10 
11 Event::assertDispatched(OrderCreated::class);
12 
13 return $order;
14 });
15 
16 // Events are dispatched as normal and observers will run ...
17 $order->update([...]);
18});
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Events\OrderCreated;
6use App\Models\Order;
7use Illuminate\Support\Facades\Event;
8use Tests\TestCase;
9 
10class ExampleTest extends TestCase
11{
12 /**
13 * Test order process.
14 */
15 public function test_orders_can_be_processed(): void
16 {
17 $order = Event::fakeFor(function () {
18 $order = Order::factory()->create();
19 
20 Event::assertDispatched(OrderCreated::class);
21 
22 return $order;
23 });
24 
25 // Events are dispatched as normal and observers will run ...
26 $order->update([...]);
27 }
28}