国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

目錄
什麼是事件和監(jiān)聽(tīng)器?
#事件
#監(jiān)聽(tīng)器
什麼是模型事件?
使用 dispatchesEvents 偵聽(tīng)模型事件
#設(shè)置模型
#創(chuàng)建事件類(lèi)
#創(chuàng)建監(jiān)聽(tīng)器
使用閉包偵聽(tīng)模型事件
使用觀察者偵聽(tīng)模型事件
測(cè)試您的模型事件
使用模型事件時(shí)的注意事項(xiàng)
要考慮的替代方法
使用模型事件的優(yōu)缺點(diǎn)
#優(yōu)點(diǎn)
#缺點(diǎn)
結(jié)論

Laravel的指南

Mar 06, 2025 am 02:25 AM

A guide to Laravel's model events

Laravel 的模型事件是一個(gè)非常方便的功能,它可以幫助您在對(duì) Eloquent 模型執(zhí)行某些操作時(shí)自動(dòng)運(yùn)行邏輯。但是,如果使用不當(dāng),有時(shí)可能會(huì)導(dǎo)致奇怪的副作用。

本文將探討模型事件是什麼以及如何在 Laravel 應(yīng)用程序中使用它們。我們還將探討如何測(cè)試模型事件以及使用它們時(shí)需要注意的一些問(wèn)題。最後,我們將介紹一些您可以考慮使用的模型事件替代方法。

什麼是事件和監(jiān)聽(tīng)器?


您可能已經(jīng)聽(tīng)說(shuō)過(guò)“事件”和“監(jiān)聽(tīng)器”。但如果您沒(méi)有聽(tīng)說(shuō)過(guò),以下是它們的簡(jiǎn)要概述:

#事件

這些是您希望在其上採(cǎi)取行動(dòng)的應(yīng)用程序中發(fā)生的事情——例如,用戶(hù)在您的網(wǎng)站上註冊(cè)、用戶(hù)登錄等。

通常,在 Laravel 中,事件是 PHP 類(lèi)。除了框架或第三方包提供的事件外,它們通常保存在 app/Events 目錄中。

以下是一個(gè)簡(jiǎn)單的事件類(lèi)的示例,您可能希望在用戶(hù)註冊(cè)到您的網(wǎng)站時(shí)調(diào)度它:

declare(strict_types=1);

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

final class UserRegistered
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public function __construct(public User $user)
    {
        //
    }
}

在上面的基本示例中,我們有一個(gè) AppEventsUserRegistered 事件類(lèi),在其構(gòu)造函數(shù)中接受一個(gè) User 模型實(shí)例。此事件類(lèi)是一個(gè)簡(jiǎn)單的容器,用於保存已註冊(cè)的用戶(hù)實(shí)例。

調(diào)度時(shí),事件將觸發(fā)任何正在偵聽(tīng)它的監(jiān)聽(tīng)器。

以下是如何在用戶(hù)註冊(cè)時(shí)調(diào)度該事件的簡(jiǎn)單示例:

use App\Events\UserRegistered;
use App\Models\User;

$user = User::create([
    'name' => 'Eric Barnes',
    'email' => 'eric@example.com',
]);

UserRegistered::dispatch($user);

在上面的示例中,我們正在創(chuàng)建一個(gè)新用戶(hù),然後使用用戶(hù)實(shí)例調(diào)度 AppEventsUserRegistered 事件。假設(shè)監(jiān)聽(tīng)器已正確註冊(cè),這將觸發(fā)任何正在偵聽(tīng) AppEventsUserRegistered 事件的監(jiān)聽(tīng)器。

#監(jiān)聽(tīng)器

監(jiān)聽(tīng)器是您希望在特定事件發(fā)生時(shí)運(yùn)行的代碼塊。

例如,堅(jiān)持我們的用戶(hù)註冊(cè)示例,您可能希望在用戶(hù)註冊(cè)時(shí)向用戶(hù)發(fā)送歡迎電子郵件。您可以創(chuàng)建一個(gè)監(jiān)聽(tīng)器來(lái)偵聽(tīng) AppEventsUserRegistered 事件並發(fā)送歡迎電子郵件。

在 Laravel 中,監(jiān)聽(tīng)器通常(但並非總是如此——稍後我們將介紹這一點(diǎn))是在 app/Listeners 目錄中找到的類(lèi)。

在用戶(hù)註冊(cè)時(shí)向用戶(hù)發(fā)送歡迎電子郵件的監(jiān)聽(tīng)器示例可能如下所示:

declare(strict_types=1);

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Mail;

final readonly class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        $event->user->notify(new WelcomeNotification());
    }
}

正如我們?cè)谏厦娴拇a示例中看到的,AppListenersSendWelcomeEmail 監(jiān)聽(tīng)器類(lèi)有一個(gè) handle 方法,它接受一個(gè) AppEventsUserRegistered 事件實(shí)例。此方法負(fù)責(zé)向用戶(hù)發(fā)送歡迎電子郵件。

有關(guān)事件和監(jiān)聽(tīng)器的更深入說(shuō)明,您可能需要查看官方文檔:http://www.miracleart.cn/link/d9a8c56824cfbe66f28f85edbbe83e09

什麼是模型事件?


在您的 Laravel 應(yīng)用程序中,您通常需要在發(fā)生某些操作時(shí)手動(dòng)調(diào)度事件。正如我們?cè)谏厦娴氖纠锌吹降?,我們可以使用以下代碼調(diào)度事件:

declare(strict_types=1);

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

final class UserRegistered
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public function __construct(public User $user)
    {
        //
    }
}

但是,當(dāng)在 Laravel 中使用 Eloquent 模型時(shí),會(huì)自動(dòng)為我們調(diào)度一些事件,因此我們不需要手動(dòng)調(diào)度它們。如果我們希望在事件發(fā)生時(shí)執(zhí)行操作,我們只需要為它們定義監(jiān)聽(tīng)器即可。

下面的列表顯示了 Eloquent 模型自動(dòng)調(diào)度的事件及其觸發(fā)器:

  • retrieved - 從數(shù)據(jù)庫(kù)中檢索。
  • creating - 正在創(chuàng)建模型。
  • created - 模型已創(chuàng)建。
  • updating - 正在更新模型。
  • updated - 模型已更新。
  • saving - 正在創(chuàng)建或更新模型。
  • saved - 模型已創(chuàng)建或更新。
  • deleting - 正在刪除模型。
  • deleted - 模型已刪除。
  • trashed - 模型已被軟刪除。
  • forceDeleting - 正在強(qiáng)制刪除模型。
  • forceDeleted - 模型已被強(qiáng)制刪除
  • restoring - 正在從軟刪除中恢復(fù)模型。
  • restored - 模型已從軟刪除中恢復(fù)。
  • replicating - 正在復(fù)制模型。

在上面的列表中,您可能會(huì)注意到一些事件名稱(chēng)相似;例如,creatingcreated。以 ing 結(jié)尾的事件在操作發(fā)生之前執(zhí)行,並且更改會(huì)持久保存到數(shù)據(jù)庫(kù)中。而以 ed 結(jié)尾的事件在操作發(fā)生之後執(zhí)行,並且更改會(huì)持久保存到數(shù)據(jù)庫(kù)中。

讓我們看看如何在 Laravel 應(yīng)用程序中使用這些模型事件。

使用 dispatchesEvents 偵聽(tīng)模型事件


偵聽(tīng)模型事件的一種方法是在您的模型上定義一個(gè) dispatchesEvents 屬性。

此屬性允許您將 Eloquent 模型事件映射到事件發(fā)生時(shí)應(yīng)調(diào)度的事件類(lèi)。這意味著您可以像處理任何其他事件一樣定義監(jiān)聽(tīng)器。

為了提供更多上下文,讓我們來(lái)看一個(gè)示例。

假設(shè)我們正在構(gòu)建一個(gè)具有兩個(gè)模型的博客應(yīng)用程序:AppModelsPostAppModelsAuthor。我們將說(shuō)這兩個(gè)模型都支持軟刪除。當(dāng)我們保存新的 AppModelsPost 時(shí),我們希望根據(jù)內(nèi)容的長(zhǎng)度計(jì)算文章的閱讀時(shí)間。當(dāng)我們軟刪除作者時(shí),我們希望軟刪除作者的所有文章。

#設(shè)置模型

我們可能有一個(gè)如下所示的 AppModelsAuthor 模型:

use App\Events\UserRegistered;
use App\Models\User;

$user = User::create([
    'name' => 'Eric Barnes',
    'email' => 'eric@example.com',
]);

UserRegistered::dispatch($user);

在上面的模型中,我們有:

  • 添加了一個(gè) dispatchesEvents 屬性,它將 deleted 模型事件映射到 AppEventsAuthorDeleted 事件類(lèi)。這意味著當(dāng)模型被刪除時(shí),將調(diào)度一個(gè)新的 AppEventsAuthorDeleted 事件。我們將在稍後創(chuàng)建此事件類(lèi)。
  • 定義了一個(gè) posts 關(guān)係。
  • 通過(guò)使用 IlluminateDatabaseEloquentSoftDeletes 特性在模型上啟用了軟刪除。

現(xiàn)在讓我們創(chuàng)建我們的 AppModelsPost 模型:

declare(strict_types=1);

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

final class UserRegistered
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public function __construct(public User $user)
    {
        //
    }
}

在上面的 AppModelsPost 模型中,我們有:

  • 添加了一個(gè) dispatchesEvents 屬性,它將 saving 模型事件映射到 AppEventsPostSaving 事件類(lèi)。這意味著當(dāng)模型被創(chuàng)建或更新時(shí),將調(diào)度一個(gè)新的 AppEventsPostSaving 事件。我們將在稍後創(chuàng)建此事件類(lèi)。
  • 定義了一個(gè) author 關(guān)係。
  • 通過(guò)使用 IlluminateDatabaseEloquentSoftDeletes 特性在模型上啟用了軟刪除。

我們的模型現(xiàn)在已準(zhǔn)備就緒,因此讓我們創(chuàng)建我們的 AppEventsAuthorDeletedAppEventsPostSaving 事件類(lèi)。

#創(chuàng)建事件類(lèi)

我們將創(chuàng)建一個(gè) AppEventsPostSaving 事件類(lèi),該類(lèi)將在保存新文章時(shí)調(diào)度:

use App\Events\UserRegistered;
use App\Models\User;

$user = User::create([
    'name' => 'Eric Barnes',
    'email' => 'eric@example.com',
]);

UserRegistered::dispatch($user);

在上面的代碼中,我們可以看到 AppEventsPostSaving 事件類(lèi),它在其構(gòu)造函數(shù)中接受一個(gè) AppModelsPost 模型實(shí)例。此事件類(lèi)是一個(gè)簡(jiǎn)單的容器,用於保存正在保存的文章實(shí)例。

類(lèi)似地,我們可以創(chuàng)建一個(gè) AppEventsAuthorDeleted 事件類(lèi),該類(lèi)將在刪除作者時(shí)調(diào)度:

declare(strict_types=1);

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Mail;

final readonly class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        $event->user->notify(new WelcomeNotification());
    }
}

在上面的 AppEventsAuthorDeleted 類(lèi)中,我們可以看到構(gòu)造函數(shù)接受一個(gè) AppModelsAuthor 模型實(shí)例。

現(xiàn)在我們可以繼續(xù)創(chuàng)建監(jiān)聽(tīng)器了。

#創(chuàng)建監(jiān)聽(tīng)器

讓我們首先創(chuàng)建一個(gè)可以用來(lái)計(jì)算文章估計(jì)閱讀時(shí)間的監(jiān)聽(tīng)器。

我們將創(chuàng)建一個(gè)新的 AppListenersCalculateReadTime 監(jiān)聽(tīng)器類(lèi):

UserRegistered::dispatch($user);

正如我們?cè)谏厦娴拇a中看到的,我們只有一個(gè) handle 方法。這是在調(diào)度 AppEventsPostSaving 事件時(shí)將自動(dòng)調(diào)用的方法。它接受 AppEventsPostSaving 事件類(lèi)的實(shí)例,其中包含正在保存的文章。

handle 方法中,我們使用一個(gè)簡(jiǎn)單的公式來(lái)計(jì)算文章的閱讀時(shí)間。在這個(gè)例子中,我們假設(shè)平均閱讀速度為每分鐘 265 個(gè)單詞。我們正在計(jì)算以秒為單位的閱讀時(shí)間,然後在文章模型上設(shè)置 read_time_in_seconds 屬性。

由於此監(jiān)聽(tīng)器將在觸發(fā) saving 模型事件時(shí)調(diào)用,這意味著每次創(chuàng)建或更新文章之前將其持久保存到數(shù)據(jù)庫(kù)中時(shí),都會(huì)計(jì)算 read_time_in_seconds 屬性。

我們還可以創(chuàng)建一個(gè)監(jiān)聽(tīng)器,在軟刪除作者時(shí)軟刪除所有相關(guān)的文章。

我們可以創(chuàng)建一個(gè)新的 AppListenersSoftDeleteAuthorRelationships 監(jiān)聽(tīng)器類(lèi):

declare(strict_types=1);

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

final class UserRegistered
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public function __construct(public User $user)
    {
        //
    }
}

在上面的監(jiān)聽(tīng)器中,handle 方法接受 AppEventsAuthorDeleted 事件類(lèi)的實(shí)例。此事件類(lèi)包含正在刪除的作者。然後,我們使用 posts 關(guān)係上的 delete 方法刪除作者的文章。

因此,每當(dāng)軟刪除 AppModelsAuthor 模型時(shí),所有作者的文章也將被軟刪除。

順便說(shuō)一句,值得注意的是,您可能希望使用更強(qiáng)大、更可重用的解決方案來(lái)實(shí)現(xiàn)此目的。但出於本文的目的,我們將其保持簡(jiǎn)單。

使用閉包偵聽(tīng)模型事件


您可以使用的另一種方法是在模型本身上定義監(jiān)聽(tīng)器作為閉包。

讓我們來(lái)看一下我們之前軟刪除作者時(shí)軟刪除文章的示例。我們可以更新我們的 AppModelsAuthor 模型以包含一個(gè)偵聽(tīng) deleted 模型事件的閉包:

use App\Events\UserRegistered;
use App\Models\User;

$user = User::create([
    'name' => 'Eric Barnes',
    'email' => 'eric@example.com',
]);

UserRegistered::dispatch($user);

我們?cè)谏厦娴哪P椭锌梢钥吹剑覀冋谀P偷?booted 方法中定義監(jiān)聽(tīng)器。我們想偵聽(tīng) deleted 模型事件,所以我們使用了 self::deleted。同樣,如果我們想為 created 模型事件創(chuàng)建一個(gè)監(jiān)聽(tīng)器,我們可以使用 self::created,依此類(lèi)推。 self::deleted 方法接受一個(gè)閉包,該閉包接收正在被刪除的 AppModelsAuthor。此閉包將在刪除模型時(shí)執(zhí)行,因此會(huì)刪除所有作者的文章。

我非常喜歡這種定義監(jiān)聽(tīng)器邏輯的方法,因?yàn)樗诖蜷_(kāi)模型類(lèi)時(shí)可以立即清楚地看到它是否註冊(cè)了觀察者。因此,儘管邏輯仍然“隱藏”在單獨(dú)的文件中,但我們可以知道我們至少為模型的一個(gè)事件註冊(cè)了監(jiān)聽(tīng)器。但是,如果這些閉包中的代碼變得更複雜,則可能值得將邏輯提取到單獨(dú)的監(jiān)聽(tīng)器類(lèi)中。

一個(gè)方便的小技巧是,您還可以使用 IlluminateEventsqueueable 函數(shù)使閉包可排隊(duì)。這意味著監(jiān)聽(tīng)器的代碼將被推送到隊(duì)列中,以便在後臺(tái)運(yùn)行,而不是在同一個(gè)請(qǐng)求生命週期中運(yùn)行。我們可以將監(jiān)聽(tīng)器更新為可排隊(duì)的,如下所示:

declare(strict_types=1);

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Mail;

final readonly class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        $event->user->notify(new WelcomeNotification());
    }
}

正如我們?cè)谏厦娴氖纠锌吹降模覀儗㈤]包包裝在 IlluminateEventsqueueable 函數(shù)中。

使用觀察者偵聽(tīng)模型事件


您可以?huà)?cǎi)取的另一種偵聽(tīng)模型事件的方法是使用模型觀察者。模型觀察者允許您在一個(gè)類(lèi)中為模型定義所有監(jiān)聽(tīng)器。

通常,它們是在 app/Observers 目錄中存在的類(lèi),並且它們具有對(duì)應(yīng)於您想要偵聽(tīng)的模型事件的方法。例如,如果您想偵聽(tīng) deleted 模型事件,您將在觀察者類(lèi)中定義一個(gè) deleted 方法。如果您想偵聽(tīng) created 模型事件,您將在觀察者類(lèi)中定義一個(gè) created 方法,依此類(lèi)推。

讓我們看看如何為我們的 AppModelsAuthor 模型創(chuàng)建一個(gè)偵聽(tīng) deleted 模型事件的模型觀察者:

declare(strict_types=1);

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

final class UserRegistered
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public function __construct(public User $user)
    {
        //
    }
}

正如我們?cè)谏厦娴拇a中看到的,我們創(chuàng)建了一個(gè)具有 deleted 方法的觀察者。此方法接受正在刪除的 AppModelsAuthor 模型的實(shí)例。然後,我們使用 posts 關(guān)係上的 delete 方法刪除作者的文章。

假設(shè),例如,我們還想為 createdupdated 模型事件定義監(jiān)聽(tīng)器。我們可以像這樣更新我們的觀察者:

use App\Events\UserRegistered;
use App\Models\User;

$user = User::create([
    'name' => 'Eric Barnes',
    'email' => 'eric@example.com',
]);

UserRegistered::dispatch($user);

為了運(yùn)行 AppObserversAuthorObserver 方法,我們需要指示 Laravel 使用它。為此,我們可以使用 #[IlluminateDatabaseEloquentAttributesObservedBy] 屬性。這允許我們將觀察者與模型關(guān)聯(lián)起來(lái),這與我們使用 #[ScopedBy] 屬性註冊(cè)全局查詢(xún)範(fàn)圍的方式類(lèi)似(如在了解如何在 Laravel 中掌握查詢(xún)範(fàn)圍中所示)。我們可以像這樣更新我們的 AppModelsAuthor 模型以使用觀察者:

declare(strict_types=1);

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Mail;

final readonly class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        $event->user->notify(new WelcomeNotification());
    }
}

我真的很喜歡這種定義監(jiān)聽(tīng)器邏輯的方式,因?yàn)樗诖蜷_(kāi)模型類(lèi)時(shí)可以立即清楚地看到它是否註冊(cè)了觀察者。因此,儘管邏輯仍然“隱藏”在單獨(dú)的文件中,但我們可以知道我們至少為模型的一個(gè)事件註冊(cè)了監(jiān)聽(tīng)器。

測(cè)試您的模型事件


無(wú)論您使用哪種模型事件方法,您都可能希望編寫(xiě)一些測(cè)試以確保您的邏輯按預(yù)期運(yùn)行。

讓我們看看如何測(cè)試我們?cè)谏厦媸纠袆?chuàng)建的模型事件。

我們首先編寫(xiě)一個(gè)測(cè)試,以確保在軟刪除作者時(shí)會(huì)軟刪除作者的文章。測(cè)試可能如下所示:

UserRegistered::dispatch($user);

在上面的測(cè)試中,我們正在創(chuàng)建一個(gè)新的作者和該作者的文章。然後我們軟刪除作者並斷言作者和文章都被軟刪除了。

這是一個(gè)非常簡(jiǎn)單但有效的測(cè)試,我們可以用它來(lái)確保我們的邏輯按預(yù)期工作。這種測(cè)試的優(yōu)點(diǎn)是它應(yīng)該適用於我們?cè)诒疚闹杏懻摰拿糠N方法。因此,如果您在我們?cè)诒疚闹杏懻摰娜魏畏椒ㄖg切換,您的測(cè)試仍然應(yīng)該通過(guò)。

同樣,我們還可以編寫(xiě)一些測(cè)試來(lái)確保在創(chuàng)建或更新文章時(shí)計(jì)算文章的閱讀時(shí)間。測(cè)試可能如下所示:

declare(strict_types=1);

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

final class UserRegistered
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public function __construct(public User $user)
    {
        //
    }
}

我們?cè)谏厦嬗袃蓚€(gè)測(cè)試:

  • 第一個(gè)測(cè)試確保在創(chuàng)建文章時(shí)計(jì)算文章的閱讀時(shí)間。
  • 第二個(gè)測(cè)試確保在更新文章時(shí)計(jì)算文章的閱讀時(shí)間。

使用模型事件時(shí)的注意事項(xiàng)


儘管模型事件非常方便,但在使用它們時(shí)需要注意一些問(wèn)題。

模型事件僅從 Eloquent 模型調(diào)度。這意味著,如果您使用 IlluminateSupportFacadesDB facade 與數(shù)據(jù)庫(kù)中模型的基礎(chǔ)數(shù)據(jù)交互,則不會(huì)調(diào)度其事件。

例如,讓我們來(lái)看一個(gè)簡(jiǎn)單的示例,我們使用 IlluminateSupportFacadesDB facade 刪除作者:

use App\Events\UserRegistered;
use App\Models\User;

$user = User::create([
    'name' => 'Eric Barnes',
    'email' => 'eric@example.com',
]);

UserRegistered::dispatch($user);

運(yùn)行上面的代碼將按預(yù)期從數(shù)據(jù)庫(kù)中刪除作者。但是,不會(huì)調(diào)度 deletingdeleted 模型事件。因此,如果您在刪除作者時(shí)為這些模型事件定義了任何監(jiān)聽(tīng)器,則不會(huì)運(yùn)行它們。

同樣,如果您使用 Eloquent 批量更新或刪除模型,則不會(huì)為受影響的模型調(diào)度 saved、updateddeletingdeleted 模型事件。這是因?yàn)槭录菑哪P捅旧碚{(diào)度的。但是,在批量更新和刪除時(shí),模型實(shí)際上並沒(méi)有從數(shù)據(jù)庫(kù)中檢索,因此不會(huì)調(diào)度事件。

例如,假設(shè)我們使用以下代碼刪除作者:

declare(strict_types=1);

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Mail;

final readonly class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        $event->user->notify(new WelcomeNotification());
    }
}

由於 delete 方法直接在查詢(xún)構(gòu)建器上調(diào)用,因此不會(huì)為該作者調(diào)度 deletingdeleted 模型事件。

要考慮的替代方法


我喜歡在我的項(xiàng)目中使用模型事件。它們作為一種很好的方法來(lái)解耦我的代碼,並且還允許我在對(duì)影響模型的代碼沒(méi)有太多控制權(quán)時(shí)自動(dòng)運(yùn)行邏輯。例如,如果我在 Laravel Nova 中刪除作者,我仍然可以在刪除作者時(shí)運(yùn)行一些邏輯。

但是,了解何時(shí)考慮使用不同的方法非常重要。

為了解釋這一點(diǎn),讓我們來(lái)看一個(gè)我們可能想要避免使用模型事件的基本示例。擴(kuò)展我們前面簡(jiǎn)單的博客應(yīng)用程序示例,假設(shè)我們希望在創(chuàng)建新文章時(shí)運(yùn)行以下操作:

  • 計(jì)算文章的閱讀時(shí)間。
  • 向 X/Twitter 發(fā)出 API 調(diào)用以分享文章。
  • 向平臺(tái)上的每個(gè)訂閱者發(fā)送通知。

因此,我們可能會(huì)創(chuàng)建三個(gè)單獨(dú)的監(jiān)聽(tīng)器(每個(gè)任務(wù)一個(gè)),這些監(jiān)聽(tīng)器每次創(chuàng)建新的 AppModelsPost 實(shí)例時(shí)都會(huì)運(yùn)行。

但是現(xiàn)在讓我們回顧一下我們之前的測(cè)試之一:

declare(strict_types=1);

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

final class UserRegistered
{
    use Dispatchable;
    use InteractsWithSockets;
    use SerializesModels;

    public function __construct(public User $user)
    {
        //
    }
}

如果我們運(yùn)行上面的測(cè)試,當(dāng)通過(guò)其工廠創(chuàng)建 AppModelsPost 模型時(shí),它還會(huì)觸發(fā)這三個(gè)操作。當(dāng)然,計(jì)算閱讀時(shí)間是一項(xiàng)次要任務(wù),因此關(guān)係不大。但我們不想在測(cè)試期間嘗試進(jìn)行 API 調(diào)用或發(fā)送通知。這些是意外的副作用。如果編寫(xiě)測(cè)試的開(kāi)發(fā)人員沒(méi)有意識(shí)到這些副作用,則可能難以追蹤這些操作發(fā)生的原因。

我們還希望避免在監(jiān)聽(tīng)器中編寫(xiě)任何特定於測(cè)試的邏輯,這會(huì)阻止這些操作在測(cè)試期間運(yùn)行。這將使應(yīng)用程序代碼更複雜,更難以維護(hù)。

這就是您可能想要考慮更明確的方法而不是依賴(lài)於自動(dòng)模型事件的情況之一。

一種方法可以是將您的 AppModelsPost 創(chuàng)建代碼提取到服務(wù)或操作類(lèi)中。例如,一個(gè)簡(jiǎn)單的服務(wù)類(lèi)可能如下所示:

use App\Events\UserRegistered;
use App\Models\User;

$user = User::create([
    'name' => 'Eric Barnes',
    'email' => 'eric@example.com',
]);

UserRegistered::dispatch($user);

在上面的類(lèi)中,我們正在手動(dòng)調(diào)用計(jì)算閱讀時(shí)間、發(fā)送通知和將其發(fā)佈到 Twitter 的代碼。這意味著我們可以更好地控制何時(shí)運(yùn)行這些操作。我們還可以輕鬆地模擬測(cè)試中的這些方法以防止它們運(yùn)行。如果需要,我們?nèi)匀豢梢耘抨?duì)這些操作(在這種情況下我們很可能會(huì)這樣做)。

因此,我們可以刪除這些操作的模型事件和監(jiān)聽(tīng)器的使用。這意味著我們可以在應(yīng)用程序代碼中使用這個(gè)新的 AppServicesPostService 類(lèi),並在測(cè)試代碼中安全地使用模型工廠。

這樣做的額外好處是它還可以使代碼更容易理解。正如我簡(jiǎn)要提到的那樣,使用事件和監(jiān)聽(tīng)器的常見(jiàn)批評(píng)是它可能會(huì)在意外的地方隱藏業(yè)務(wù)邏輯。因此,如果新的開(kāi)發(fā)人員加入團(tuán)隊(duì),如果它們由模型事件觸發(fā),他們可能不知道某些操作在哪里或?yàn)槭颤N發(fā)生。

但是,如果您仍然希望為此類(lèi)邏輯使用事件和監(jiān)聽(tīng)器,您可以考慮使用更明確的方法。例如,您可以從服務(wù)類(lèi)調(diào)度一個(gè)事件來(lái)觸發(fā)監(jiān)聽(tīng)器。這樣,您仍然可以使用事件和監(jiān)聽(tīng)器的解耦優(yōu)勢(shì),但您可以更好地控制何時(shí)調(diào)度事件。

例如,我們可以更新上面我們 AppServicesPostService 示例中的 createPost 方法以調(diào)度事件:

declare(strict_types=1);

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Notifications\WelcomeNotification;
use Illuminate\Support\Facades\Mail;

final readonly class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        $event->user->notify(new WelcomeNotification());
    }
}

通過(guò)使用上述方法,我們?nèi)匀豢梢該碛袉为?dú)的監(jiān)聽(tīng)器來(lái)向 Twitter 發(fā)出 API 請(qǐng)求並發(fā)送通知。但是我們可以更好地控制何時(shí)運(yùn)行這些操作,因此在使用模型工廠進(jìn)行測(cè)試時(shí)不會(huì)運(yùn)行它們。

在決定使用這些方法中的任何一種時(shí),沒(méi)有任何黃金法則。這完全取決於您、您的團(tuán)隊(duì)以及您正在構(gòu)建的功能。但是,我傾向於遵循以下經(jīng)驗(yàn)法則:

  • 如果監(jiān)聽(tīng)器中的操作僅對(duì)模型進(jìn)行細(xì)微更改,請(qǐng)考慮使用模型事件。示例:生成 slug、計(jì)算閱讀時(shí)間等。
  • 如果操作將影響另一個(gè)模型(無(wú)論是自動(dòng)創(chuàng)建、更新還是刪除),則更明確一些,不要使用模型事件。
  • 如果操作將與外部進(jìn)程(API 調(diào)用、文件處理、觸發(fā)通知、排隊(duì)作業(yè))一起工作,則更明確一些,不要使用模型事件。

使用模型事件的優(yōu)缺點(diǎn)


為了快速總結(jié)我們?cè)诒疚闹薪榻B的內(nèi)容,以下是使用模型事件的一些優(yōu)缺點(diǎn):

#優(yōu)點(diǎn)

  • 鼓勵(lì)您解耦代碼。
  • 允許您自動(dòng)觸發(fā)操作,無(wú)論模型在哪裡創(chuàng)建/更新/刪除。例如,如果模型是在 Laravel Nova 中創(chuàng)建的,您可以觸發(fā)業(yè)務(wù)邏輯。
  • 您無(wú)需記住每次創(chuàng)建/更新/刪除模型時(shí)都調(diào)度事件。

#缺點(diǎn)

  • 可能導(dǎo)致意外的副作用。您可能希望創(chuàng)建/更新/刪除模型而不會(huì)觸發(fā)某些監(jiān)聽(tīng)器,但這可能會(huì)導(dǎo)致意外行為。在編寫(xiě)測(cè)試時(shí),這尤其成問(wèn)題。
  • 可能會(huì)在難以追蹤的意外位置隱藏業(yè)務(wù)邏輯。這會(huì)使代碼流程更難理解。

結(jié)論


希望本文能為您概述模型事件是什麼以及使用它們的各種方法。它還應(yīng)該向您展示瞭如何測(cè)試模型事件代碼以及使用它們時(shí)需要注意的一些問(wèn)題。

您現(xiàn)在應(yīng)該有足夠的信心在您的 Laravel 應(yīng)用程序中使用模型事件了。

以上是Laravel的指南的詳細(xì)內(nèi)容。更多資訊請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

本網(wǎng)站聲明
本文內(nèi)容由網(wǎng)友自願(yuàn)投稿,版權(quán)歸原作者所有。本站不承擔(dān)相應(yīng)的法律責(zé)任。如發(fā)現(xiàn)涉嫌抄襲或侵權(quán)的內(nèi)容,請(qǐng)聯(lián)絡(luò)admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費(fèi)脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅(qū)動(dòng)的應(yīng)用程序,用於創(chuàng)建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費(fèi)的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費(fèi)的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強(qiáng)大的PHP整合開(kāi)發(fā)環(huán)境

Dreamweaver CS6

Dreamweaver CS6

視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版

SublimeText3 Mac版

神級(jí)程式碼編輯軟體(SublimeText3)

熱門(mén)話(huà)題

如何在PHP中實(shí)施身份驗(yàn)證和授權(quán)? 如何在PHP中實(shí)施身份驗(yàn)證和授權(quán)? Jun 20, 2025 am 01:03 AM

tosecurelyhandleauthenticationandationallizationInphp,lofterTheSesteps:1.AlwaysHashPasswordSwithPassword_hash()andverifyusingspasspassword_verify(),usepreparedStatatementStopreventsqlineptions,andStoreSeruserDatain usseruserDatain $ _sessiveferterlogin.2.implementrole-2.imaccessccsccccccccccccccccccccccccc.

如何在PHP中安全地處理文件上傳? 如何在PHP中安全地處理文件上傳? Jun 19, 2025 am 01:05 AM

要安全處理PHP中的文件上傳,核心在於驗(yàn)證文件類(lèi)型、重命名文件並限制權(quán)限。 1.使用finfo_file()檢查真實(shí)MIME類(lèi)型,僅允許特定類(lèi)型如image/jpeg;2.用uniqid()生成隨機(jī)文件名,存儲(chǔ)至非Web根目錄;3.通過(guò)php.ini和HTML表單限製文件大小,設(shè)置目錄權(quán)限為0755;4.使用ClamAV掃描惡意軟件,增強(qiáng)安全性。這些步驟有效防止安全漏洞,確保文件上傳過(guò)程安全可靠。

PHP中==(鬆散比較)和===(嚴(yán)格的比較)之間有什麼區(qū)別? PHP中==(鬆散比較)和===(嚴(yán)格的比較)之間有什麼區(qū)別? Jun 19, 2025 am 01:07 AM

在PHP中,==與===的主要區(qū)別在於類(lèi)型檢查的嚴(yán)格程度。 ==在比較前會(huì)進(jìn)行類(lèi)型轉(zhuǎn)換,例如5=="5"返回true,而===要求值和類(lèi)型都相同才會(huì)返回true,例如5==="5"返回false。使用場(chǎng)景上,===更安全應(yīng)優(yōu)先使用,==僅在需要類(lèi)型轉(zhuǎn)換時(shí)使用。

如何在PHP( - , *, /,%)中執(zhí)行算術(shù)操作? 如何在PHP( - , *, /,%)中執(zhí)行算術(shù)操作? Jun 19, 2025 pm 05:13 PM

PHP中使用基本數(shù)學(xué)運(yùn)算的方法如下:1.加法用 號(hào),支持整數(shù)和浮點(diǎn)數(shù),也可用於變量,字符串?dāng)?shù)字會(huì)自動(dòng)轉(zhuǎn)換但不推薦依賴(lài);2.減法用-號(hào),變量同理,類(lèi)型轉(zhuǎn)換同樣適用;3.乘法用*號(hào),適用於數(shù)字及類(lèi)似字符串;4.除法用/號(hào),需避免除以零,並註意結(jié)果可能是浮點(diǎn)數(shù);5.取模用%號(hào),可用於判斷奇偶數(shù),處理負(fù)數(shù)時(shí)餘數(shù)符號(hào)與被除數(shù)一致。正確使用這些運(yùn)算符的關(guān)鍵在於確保數(shù)據(jù)類(lèi)型清晰並處理好邊界情況。

如何與PHP的NOSQL數(shù)據(jù)庫(kù)(例如MongoDB,Redis)進(jìn)行交互? 如何與PHP的NOSQL數(shù)據(jù)庫(kù)(例如MongoDB,Redis)進(jìn)行交互? Jun 19, 2025 am 01:07 AM

是的,PHP可以通過(guò)特定擴(kuò)展或庫(kù)與MongoDB和Redis等NoSQL數(shù)據(jù)庫(kù)交互。首先,使用MongoDBPHP驅(qū)動(dòng)(通過(guò)PECL或Composer安裝)創(chuàng)建客戶(hù)端實(shí)例並操作數(shù)據(jù)庫(kù)及集合,支持插入、查詢(xún)、聚合等操作;其次,使用Predis庫(kù)或phpredis擴(kuò)展連接Redis,執(zhí)行鍵值設(shè)置與獲取,推薦phpredis用於高性能場(chǎng)景,Predis則便於快速部署;兩者均適用於生產(chǎn)環(huán)境且文檔完善。

我如何了解最新的PHP開(kāi)發(fā)和最佳實(shí)踐? 我如何了解最新的PHP開(kāi)發(fā)和最佳實(shí)踐? Jun 23, 2025 am 12:56 AM

TostaycurrentwithPHPdevelopmentsandbestpractices,followkeynewssourceslikePHP.netandPHPWeekly,engagewithcommunitiesonforumsandconferences,keeptoolingupdatedandgraduallyadoptnewfeatures,andreadorcontributetoopensourceprojects.First,followreliablesource

什麼是PHP,為什麼它用於Web開(kāi)發(fā)? 什麼是PHP,為什麼它用於Web開(kāi)發(fā)? Jun 23, 2025 am 12:55 AM

PHPbecamepopularforwebdevelopmentduetoitseaseoflearning,seamlessintegrationwithHTML,widespreadhostingsupport,andalargeecosystemincludingframeworkslikeLaravelandCMSplatformslikeWordPress.Itexcelsinhandlingformsubmissions,managingusersessions,interacti

如何設(shè)置PHP時(shí)區(qū)? 如何設(shè)置PHP時(shí)區(qū)? Jun 25, 2025 am 01:00 AM

tosetTherightTimeZoneInphp,restate_default_timezone_set()functionAtthestArtofyourscriptWithavalIdidentIdentifiersuchas'america/new_york'.1.usedate_default_default_timezone_set_set()

See all articles