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

目錄
什么是事件和監(jiān)聽(tīng)器?
#事件
#監(jiān)聽(tīng)器
什么是模型事件?
使用 dispatchesEvents 偵聽(tīng)模型事件
#設(shè)置模型
#創(chuàng)建事件類
#創(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)要概述:

#事件

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

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

以下是一個(gè)簡(jiǎn)單的事件類的示例,您可能希望在用戶注冊(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 事件類,在其構(gòu)造函數(shù)中接受一個(gè) User 模型實(shí)例。此事件類是一個(gè)簡(jiǎn)單的容器,用于保存已注冊(cè)的用戶實(shí)例。

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

以下是如何在用戶注冊(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è)新用戶,然后使用用戶實(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)持我們的用戶注冊(cè)示例,您可能希望在用戶注冊(cè)時(shí)向用戶發(fā)送歡迎電子郵件。您可以創(chuàng)建一個(gè)監(jiān)聽(tīng)器來(lái)偵聽(tīng) AppEventsUserRegistered 事件并發(fā)送歡迎電子郵件。

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

在用戶注冊(cè)時(shí)向用戶發(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)器類有一個(gè) handle 方法,它接受一個(gè) AppEventsUserRegistered 事件實(shí)例。此方法負(fù)責(zé)向用戶發(fā)送歡迎電子郵件。

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

什么是模型事件?


在您的 Laravel 應(yīng)用程序中,您通常需要在發(fā)生某些操作時(shí)手動(dòng)調(diào)度事件。正如我們?cè)谏厦娴氖纠锌吹降模覀兛梢允褂靡韵麓a調(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ì)注意到一些事件名稱相似;例如,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)度的事件類。這意味著您可以像處理任何其他事件一樣定義監(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 事件類。這意味著當(dāng)模型被刪除時(shí),將調(diào)度一個(gè)新的 AppEventsAuthorDeleted 事件。我們將在稍后創(chuàng)建此事件類。
  • 定義了一個(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 事件類。這意味著當(dāng)模型被創(chuàng)建或更新時(shí),將調(diào)度一個(gè)新的 AppEventsPostSaving 事件。我們將在稍后創(chuàng)建此事件類。
  • 定義了一個(gè) author 關(guān)系。
  • 通過(guò)使用 IlluminateDatabaseEloquentSoftDeletes 特性在模型上啟用了軟刪除。

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

#創(chuàng)建事件類

我們將創(chuàng)建一個(gè) AppEventsPostSaving 事件類,該類將在保存新文章時(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 事件類,它在其構(gòu)造函數(shù)中接受一個(gè) AppModelsPost 模型實(shí)例。此事件類是一個(gè)簡(jiǎn)單的容器,用于保存正在保存的文章實(shí)例。

類似地,我們可以創(chuàng)建一個(gè) AppEventsAuthorDeleted 事件類,該類將在刪除作者時(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 類中,我們可以看到構(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)器類:

UserRegistered::dispatch($user);

正如我們?cè)谏厦娴拇a中看到的,我們只有一個(gè) handle 方法。這是在調(diào)度 AppEventsPostSaving 事件時(shí)將自動(dòng)調(diào)用的方法。它接受 AppEventsPostSaving 事件類的實(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)器類:

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 事件類的實(shí)例。此事件類包含正在刪除的作者。然后,我們使用 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椭锌梢钥吹?,我們正在模型?booted 方法中定義監(jiān)聽(tīng)器。我們想偵聽(tīng) deleted 模型事件,所以我們使用了 self::deleted。同樣,如果我們想為 created 模型事件創(chuàng)建一個(gè)監(jiān)聽(tīng)器,我們可以使用 self::created,依此類推。self::deleted 方法接受一個(gè)閉包,該閉包接收正在被刪除的 AppModelsAuthor。此閉包將在刪除模型時(shí)執(zhí)行,因此會(huì)刪除所有作者的文章。

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

一個(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)模型事件


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

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

讓我們看看如何為我們的 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è)全局查詢范圍的方式類似(如在了解如何在 Laravel 中掌握查詢范圍中所示)。我們可以像這樣更新我們的 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)模型類時(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)度 savedupdated、deletingdeleted 模型事件。這是因?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 方法直接在查詢構(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)用程序代碼更復(fù)雜,更難以維護(hù)。

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

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

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

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

UserRegistered::dispatch($user);

在上面的類中,我們正在手動(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 類,并在測(cè)試代碼中安全地使用模型工廠。

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

但是,如果您仍然希望為此類邏輯使用事件和監(jiān)聽(tīng)器,您可以考慮使用更明確的方法。例如,您可以從服務(wù)類調(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)文章!

本站聲明
本文內(nèi)容由網(wǎng)友自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請(qǐng)聯(lián)系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脫衣機(jī)

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)話題

Laravel 教程
1600
29
PHP教程
1502
276
PHP變量范圍解釋了 PHP變量范圍解釋了 Jul 17, 2025 am 04:16 AM

PHP變量作用域常見(jiàn)問(wèn)題及解決方法包括:1.函數(shù)內(nèi)部無(wú)法訪問(wèn)全局變量,需使用global關(guān)鍵字或參數(shù)傳入;2.靜態(tài)變量用static聲明,只初始化一次并在多次調(diào)用間保持值;3.超全局變量如$_GET、$_POST可在任何作用域直接使用,但需注意安全過(guò)濾;4.匿名函數(shù)需通過(guò)use關(guān)鍵字引入父作用域變量,修改外部變量則需傳遞引用。掌握這些規(guī)則有助于避免錯(cuò)誤并提升代碼穩(wěn)定性。

如何在PHP中牢固地處理文件上傳? 如何在PHP中牢固地處理文件上傳? Jul 08, 2025 am 02:37 AM

要安全處理PHP文件上傳需驗(yàn)證來(lái)源與類型、控制文件名與路徑、設(shè)置服務(wù)器限制并二次處理媒體文件。1.驗(yàn)證上傳來(lái)源通過(guò)token防止CSRF并通過(guò)finfo_file檢測(cè)真實(shí)MIME類型使用白名單控制;2.重命名文件為隨機(jī)字符串并根據(jù)檢測(cè)類型決定擴(kuò)展名存儲(chǔ)至非Web目錄;3.PHP配置限制上傳大小及臨時(shí)目錄Nginx/Apache禁止訪問(wèn)上傳目錄;4.GD庫(kù)重新保存圖片清除潛在惡意數(shù)據(jù)。

在PHP中評(píng)論代碼 在PHP中評(píng)論代碼 Jul 18, 2025 am 04:57 AM

PHP注釋代碼常用方法有三種:1.單行注釋用//或#屏蔽一行代碼,推薦使用//;2.多行注釋用/.../包裹代碼塊,不可嵌套但可跨行;3.組合技巧注釋如用/if(){}/控制邏輯塊,或配合編輯器快捷鍵提升效率,使用時(shí)需注意閉合符號(hào)和避免嵌套。

發(fā)電機(jī)如何在PHP中工作? 發(fā)電機(jī)如何在PHP中工作? Jul 11, 2025 am 03:12 AM

AgeneratorinPHPisamemory-efficientwaytoiterateoverlargedatasetsbyyieldingvaluesoneatatimeinsteadofreturningthemallatonce.1.Generatorsusetheyieldkeywordtoproducevaluesondemand,reducingmemoryusage.2.Theyareusefulforhandlingbigloops,readinglargefiles,or

撰寫(xiě)PHP評(píng)論的提示 撰寫(xiě)PHP評(píng)論的提示 Jul 18, 2025 am 04:51 AM

寫(xiě)好PHP注釋的關(guān)鍵在于明確目的與規(guī)范,注釋?xiě)?yīng)解釋“為什么”而非“做了什么”,避免冗余或過(guò)于簡(jiǎn)單。1.使用統(tǒng)一格式,如docblock(/*/)用于類、方法說(shuō)明,提升可讀性與工具兼容性;2.強(qiáng)調(diào)邏輯背后的原因,如說(shuō)明為何需手動(dòng)輸出JS跳轉(zhuǎn);3.在復(fù)雜代碼前添加總覽性說(shuō)明,分步驟描述流程,幫助理解整體思路;4.合理使用TODO和FIXME標(biāo)記待辦事項(xiàng)與問(wèn)題,便于后續(xù)追蹤與協(xié)作。好的注釋能降低溝通成本,提升代碼維護(hù)效率。

學(xué)習(xí)PHP:初學(xué)者指南 學(xué)習(xí)PHP:初學(xué)者指南 Jul 18, 2025 am 04:54 AM

易于效率,啟動(dòng)啟動(dòng)tingupalocalserverenverenvirestoolslikexamppandacodeeditorlikevscode.1)installxamppforapache,mysql,andphp.2)uscodeeditorforsyntaxssupport.3)

如何通過(guò)php中的索引訪問(wèn)字符串中的字符 如何通過(guò)php中的索引訪問(wèn)字符串中的字符 Jul 12, 2025 am 03:15 AM

在PHP中獲取字符串特定索引字符可用方括號(hào)或花括號(hào),但推薦方括號(hào);索引從0開(kāi)始,超出范圍訪問(wèn)返回空值,不可賦值;處理多字節(jié)字符需用mb_substr。例如:$str="hello";echo$str[0];輸出h;而中文等字符需用mb_substr($str,1,1)獲取正確結(jié)果;實(shí)際應(yīng)用中循環(huán)訪問(wèn)前應(yīng)檢查字符串長(zhǎng)度,動(dòng)態(tài)字符串需驗(yàn)證有效性,多語(yǔ)言項(xiàng)目建議統(tǒng)一使用多字節(jié)安全函數(shù)。

快速PHP安裝教程 快速PHP安裝教程 Jul 18, 2025 am 04:52 AM

ToinstallPHPquickly,useXAMPPonWindowsorHomebrewonmacOS.1.OnWindows,downloadandinstallXAMPP,selectcomponents,startApache,andplacefilesinhtdocs.2.Alternatively,manuallyinstallPHPfromphp.netandsetupaserverlikeApache.3.OnmacOS,installHomebrew,thenrun'bre

See all articles