Laravel 模型事件指南
上次更新于 作者: Ashley Allen
模型事件是 Laravel 中一个非常方便的功能,它可以帮助你自动运行逻辑,当你的 Eloquent 模型执行某些操作时。但如果使用不当,有时会导致奇怪的副作用。
在这篇文章中,我们将介绍模型事件是什么以及如何在你的 Laravel 应用程序中使用它们。我们还将介绍如何测试模型事件以及使用它们时需要注意的一些问题。最后,我们将介绍一些替代模型事件的方法,你可能希望考虑使用它们。
什么是事件和监听器?
你可能已经听说过“事件”和“监听器”。但如果你还没有,这里简要介绍一下它们是什么
事件
这些是在你的应用程序中发生的你想要处理的事情——例如,用户在你的网站上注册、用户登录等等。
通常,在 Laravel 中,事件是 PHP 类。除了框架或第三方包提供的事件外,它们通常保存在 app/Events
目录中。
以下是一个简单的事件类示例,你可能希望在用户在你的网站上注册时派发它
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) { // }}
在上面的基本示例中,我们有一个 App\Events\UserRegistered
事件类,它在构造函数中接受一个 User
模型实例。这个事件类是一个简单的數據容器,它保存着已注册的用户实例。
派发时,该事件将触发任何监听它的监听器。
以下是一个简单示例,说明如何在用户注册时派发该事件
use App\Events\UserRegistered;use App\Models\User; $user = User::create([ 'name' => 'Eric Barnes',]); UserRegistered::dispatch($user);
在上面的示例中,我们正在创建一个新用户,然后用用户实例派发 App\Events\UserRegistered
事件。假设监听器已正确注册,这将触发任何监听 App\Events\UserRegistered
事件的监听器。
监听器
监听器是你想要在特定事件发生时运行的代码块。
例如,坚持我们的用户注册示例,你可能希望在用户注册时向用户发送欢迎邮件。你可以创建一个监听器,监听 App\Events\UserRegistered
事件并发送欢迎邮件。
在 Laravel 中,监听器通常(但并不总是——我们将在后面介绍)是 app/Listeners
目录中的类。
一个监听器示例,它在用户注册时向用户发送欢迎邮件,可能如下所示
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()); }}
正如我们在上面的代码示例中看到的,App\Listeners\SendWelcomeEmail
监听器类有一个 handle
方法,它接受一个 App\Events\UserRegistered
事件实例。该方法负责向用户发送欢迎邮件。
有关事件和监听器的更深入解释,你可能需要查看官方文档:https://laravel.net.cn/docs/11.x/events
什么是模型事件?
在你的 Laravel 应用程序中,你通常需要在发生特定操作时手动派发事件。正如我们在上面的示例中看到的,我们可以使用以下代码派发事件
UserRegistered::dispatch($user);
但是,在 Laravel 中使用 Eloquent 模型时,有一些事件会自动为我们派发,因此我们不需要手动派发它们。我们只需要为它们定义监听器,如果我们想在它们发生时执行操作。
以下列表显示了 Eloquent 模型自动派发的事件以及它们的触发器
- retrieved - 从数据库中检索。
- creating - 正在创建模型。
- created - 模型已创建。
- updating - 正在更新模型。
- updated - 模型已更新。
- saving - 正在创建或更新模型。
- saved - 模型已创建或更新。
- deleting - 正在删除模型。
- deleted - 模型已删除。
- trashed - 模型已被软删除。
- forceDeleting - 正在强制删除模型。
- forceDeleted - 模型已被强制删除
- restoring - 模型正在从软删除中恢复。
- restored - 模型已从软删除中恢复。
- replicating - 模型正在被复制。
在上面的列表中,你可能注意到一些事件名称很相似;例如,creating
和 created
。以 ing
结尾的事件在操作发生之前执行,并且更改已持久保存到数据库中。而以 ed
结尾的事件在操作发生之后执行,并且更改已持久保存到数据库中。
让我们看看如何在我们的 Laravel 应用程序中使用这些模型事件。
dispatchesEvents
监听模型事件
使用 监听模型事件的一种方法是在你的模型上定义一个 dispatchesEvents
属性。
此属性允许你将 Eloquent 模型事件映射到事件发生时应该派发的事件类。这意味着你可以在需要时像处理其他事件一样定义你的监听器。
为了提供更多上下文,让我们看一个示例。
假设我们正在构建一个具有两个模型的博客应用程序:App\Models\Post
和 App\Models\Author
。我们假设这两个模型都支持软删除。当我们保存一个新的 App\Models\Post
时,我们希望根据内容的长度计算文章的阅读时间。当我们软删除作者时,我们希望软删除作者的所有文章。
设置模型
我们可能有一个 App\Models\Author
模型,如下所示
declare(strict_types=1); namespace App\Models; use App\Events\AuthorDeleted;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\HasMany;use Illuminate\Database\Eloquent\SoftDeletes; final class Author extends Model{ use HasFactory; use SoftDeletes; protected $dispatchesEvents = [ 'deleted' => AuthorDeleted::class, ]; public function posts(): HasMany { return $this->hasMany(Post::class); }}
在上面的模型中,我们有
- 添加了一个
dispatchesEvents
属性,它将deleted
模型事件映射到App\Events\AuthorDeleted
事件类。这意味着当模型被删除时,将派发一个新的App\Events\AuthorDeleted
事件。我们将在稍后创建此事件类。 - 定义了一个
posts
关系。 - 通过使用
Illuminate\Database\Eloquent\SoftDeletes
特性,在模型上启用了软删除。
现在让我们创建我们的 App\Models\Post
模型
declare(strict_types=1); namespace App\Models; use App\Events\PostSaving;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo;use Illuminate\Database\Eloquent\SoftDeletes; final class Post extends Model{ use HasFactory; use SoftDeletes; protected $dispatchesEvents = [ 'saving' => PostSaving::class, ]; public function author(): BelongsTo { return $this->belongsTo(Author::class); }}
在上面的 App\Models\Post
模型中,我们有
- 添加了一个
dispatchesEvents
属性,它将saving
模型事件映射到App\Events\PostSaving
事件类。这意味着当模型被创建或更新时,将派发一个新的App\Events\PostSaving
事件。我们将在稍后创建此事件类。 - 定义了一个
author
关系。 - 通过使用
Illuminate\Database\Eloquent\SoftDeletes
特性,在模型上启用了软删除。
我们的模型现在已准备就绪,所以让我们创建我们的 App\Events\AuthorDeleted
和 App\Events\PostSaving
事件类。
创建事件类
我们将创建一个 App\Events\PostSaving
事件类,它将在保存新文章时派发
declare(strict_types=1); namespace App\Events; use App\Models\Post;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Foundation\Events\Dispatchable;use Illuminate\Queue\SerializesModels; final class PostSaving{ use Dispatchable; use InteractsWithSockets; use SerializesModels; public function __construct(public Post $post) { // }}
在上面的代码中,我们可以看到 App\Events\PostSaving
事件类,它在构造函数中接受一个 App\Models\Post
模型实例。这个事件类是一个简单的數據容器,它保存着正在保存的文章实例。
类似地,我们可以创建一个 App\Events\AuthorDeleted
事件类,它将在删除作者时派发
declare(strict_types=1); namespace App\Events; use App\Models\Author;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Foundation\Events\Dispatchable;use Illuminate\Queue\SerializesModels; final class AuthorDeleted{ use Dispatchable; use InteractsWithSockets; use SerializesModels; public function __construct(public Author $author) { // }}
在上面的 App\Events\AuthorDeleted
类中,我们可以看到构造函数接受一个 App\Models\Author
模型实例。
现在我们可以继续创建我们的监听器。
创建监听器
首先,让我们创建一个监听器,它可以用来计算帖子的预计阅读时间。
我们将创建一个新的 App\Listeners\CalculateReadTime
监听器类
declare(strict_types=1); namespace App\Listeners; use App\Events\PostSaving;use Illuminate\Support\Str; final readonly class CalculateReadTime{ public function handle(PostSaving $event): void { $event->post->read_time_in_seconds = (int) ceil( (Str::wordCount($event->post->content) / 265) * 60 ); }}
从上面的代码可以看到,我们有一个 handle
方法。当 App\Events\PostSaving
事件被派发时,这个方法会被自动调用。它接收一个 App\Events\PostSaving
事件类实例,该实例包含正在保存的帖子。
在 handle
方法中,我们使用了一个简单的公式来计算帖子的阅读时间。在这种情况下,我们假设平均阅读速度是每分钟 265 个字。我们以秒为单位计算阅读时间,然后将 read_time_in_seconds
属性设置到帖子模型上。
由于这个监听器将在 saving
模型事件被触发时被调用,这意味着每次创建或更新帖子并在将其持久化到数据库之前,read_time_in_seconds
属性都会被计算。
我们还可以创建一个监听器,当作者被软删除时,它会软删除所有相关的帖子。
我们可以创建一个新的 App\Listeners\SoftDeleteAuthorRelationships
监听器类
declare(strict_types=1); namespace App\Listeners; use App\Events\AuthorDeleted; final readonly class SoftDeleteAuthorRelationships{ public function handle(AuthorDeleted $event): void { $event->author->posts()->delete(); // Soft delete any other relationships here... }}
在上面的监听器中,handle
方法接收一个 App\Events\AuthorDeleted
事件类实例。这个事件类包含正在被删除的作者。然后,我们使用 posts
关系上的 delete
方法来删除作者的帖子。
因此,每当 App\Models\Author
模型被软删除时,所有作者的帖子也会被软删除。
作为旁注,值得注意的是,你可能希望使用更健壮、可重用的解决方案来实现这一点。但是为了本文的目的,我们将其保持简单。
使用闭包监听模型事件
你可以使用的另一种方法是在模型本身定义监听器作为闭包。
让我们以之前删除作者时软删除帖子的例子为例。我们可以更新我们的 App\Models\Author
模型,让它包含一个监听 deleted
模型事件的闭包
declare(strict_types=1); namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\HasMany;use Illuminate\Database\Eloquent\SoftDeletes; final class Author extends Model{ use HasFactory; use SoftDeletes; protected static function booted(): void { self::deleted(static function (Author $author): void { $author->posts()->delete(); }); } public function posts(): HasMany { return $this->hasMany(Post::class); }}
从上面的模型可以看到,我们在模型的 booted
方法中定义了我们的监听器。我们想要监听 deleted
模型事件,所以我们使用了 self::deleted
。同样,如果我们想要为 created
模型事件创建一个监听器,我们可以使用 self::created
,等等。self::deleted
方法接受一个闭包,该闭包接收正在被删除的 App\Models\Author
。当模型被删除时,这个闭包将被执行,从而删除所有作者的帖子。
我非常喜欢这种方法,因为它适用于非常简单的监听器。它将逻辑保留在模型类内部,这样开发人员更容易看到它。有时,将逻辑提取到单独的监听器类中会使代码更难理解和跟踪,这可能会使代码逻辑难以跟踪,尤其是在你不熟悉代码库的情况下。但是,如果这些闭包内部的代码变得更复杂,可能值得将逻辑提取到单独的监听器类中。
一个需要了解的实用技巧是,你还可以使用 Illuminate\Events\queueable
函数使闭包可排队。这意味着监听器的代码将被推送到队列中,在后台运行而不是在同一个请求生命周期内运行。我们可以像这样更新我们的监听器,使其可排队
declare(strict_types=1); namespace App\Models; use Illuminate\Database\Eloquent\Model;use function Illuminate\Events\queueable; final class Author extends Model{ // ... protected static function booted(): void { self::deleted(queueable(static function (Author $author): void { $author->posts()->delete(); })); } // ...}
从上面的例子可以看到,我们使用 Illuminate\Events\queueable
函数将闭包包装起来。
使用观察者监听模型事件
你可以采取的另一种监听模型事件的方法是使用模型观察者。模型观察者允许你在一个类中定义所有模型的监听器。
通常,它们是位于 app/Observers
目录中的类,并且它们具有与你想要监听的模型事件相对应的方法。例如,如果你想要监听 deleted
模型事件,你将在你的观察者类中定义一个 deleted
方法。如果你想要监听 created
模型事件,你将在你的观察者类中定义一个 created
方法,等等。
让我们看看如何为我们的 App\Models\Author
模型创建一个模型观察者,该观察者监听 deleted
模型事件
declare(strict_types=1); namespace App\Observers; use App\Models\Author; final readonly class AuthorObserver{ public function deleted(Author $author): void { $author->posts()->delete(); }}
从上面的代码可以看到,我们创建了一个观察者,它有一个 deleted
方法。这个方法接受正在被删除的 App\Models\Author
模型实例。然后,我们使用 posts
关系上的 delete
方法来删除作者的帖子。
假设,作为示例,我们还想要为 created
和 updated
模型事件定义监听器。我们可以像这样更新我们的观察者
declare(strict_types=1); namespace App\Observers; use App\Models\Author; final readonly class AuthorObserver{ public function created(Author $author): void { // Logic to run when the author is created... } public function updated(Author $author): void { // Logic to run when the author is updated... } public function deleted(Author $author): void { $author->posts()->delete(); }}
为了让 App\Observers\AuthorObserver
方法运行,我们需要指示 Laravel 使用它。为此,我们可以使用 #[Illuminate\Database\Eloquent\Attributes\ObservedBy]
属性。这允许我们将观察者与模型关联起来,类似于我们使用 #[ScopedBy]
属性注册全局查询范围的方式(如在 了解如何在 Laravel 中掌握查询范围 中所示)。我们可以像这样更新我们的 App\Models\Author
模型来使用观察者
declare(strict_types=1); namespace App\Models; use App\Observers\AuthorObserver;use Illuminate\Database\Eloquent\Attributes\ObservedBy;use Illuminate\Database\Eloquent\Model; #[ObservedBy(AuthorObserver::class)]final class Author extends Model{ // ...}
我非常喜欢这种定义监听器逻辑的方式,因为它在打开模型类时可以立即清楚地看到它注册了一个观察者。因此,尽管逻辑仍然“隐藏”在单独的文件中,但我们可以意识到我们至少有一个模型事件的监听器。
测试你的模型事件
无论你使用哪种模型事件方法,你都可能想编写一些测试来确保你的逻辑按预期运行。
让我们看看如何测试我们在上面的例子中创建的模型事件。
首先,我们将编写一个测试,确保当作者被软删除时,作者的帖子也被软删除。该测试可能类似于
declare(strict_types=1); namespace Tests\Feature\Models; use App\Models\Author;use App\Models\Post;use Illuminate\Foundation\Testing\LazilyRefreshDatabase;use PHPUnit\Framework\Attributes\Test;use Tests\TestCase; final class AuthorTest extends TestCase{ use LazilyRefreshDatabase; #[Test] public function author_can_be_soft_deleted(): void { // Create our author and post. $author = Author::factory()->create(); $post = Post::factory()->for($author)->create(); // Delete the author. $author->delete(); // Assert the author and their associated post // is soft-deleted. $this->assertSoftDeleted($author); $this->assertSoftDeleted($post); }}
在上面的测试中,我们创建了一个新的作者和该作者的帖子。然后我们软删除作者,并断言作者和帖子都被软删除了。
这是一个非常简单但有效的测试,我们可以用它来确保我们的逻辑按预期工作。这种测试的妙处在于,它应该适用于本文中讨论的所有方法。因此,如果你在本文中讨论的任何方法之间切换,你的测试应该仍然通过。
同样,我们还可以编写一些测试来确保当帖子被创建或更新时,会计算帖子的阅读时间。测试可能类似于
declare(strict_types=1); namespace Tests\Feature\Models; use App\Models\Author;use App\Models\Post;use Illuminate\Foundation\Testing\LazilyRefreshDatabase;use PHPUnit\Framework\Attributes\Test;use Tests\TestCase; final class PostTest extends TestCase{ use LazilyRefreshDatabase; #[Test] public function read_time_is_calculated_when_storing_post(): void { $post = Post::factory() ->for(Author::factory()) ->create([ 'content' => 'This is a post with some content.' ]); $this->assertSame(2, $post->read_time_in_seconds); } #[Test] public function read_time_is_calculated_when_updating_post(): void { $post = Post::factory() ->for(Author::factory()) ->create(); $post->content = 'This is a post with some content. ...'; $post->save(); $this->assertSame(8, $post->read_time_in_seconds); }}
我们在上面有两个测试
- 第一个测试确保在帖子被创建时,会计算帖子的阅读时间。
- 第二个测试确保在帖子被更新时,会计算帖子的阅读时间。
使用模型事件时的注意事项
虽然模型事件非常方便,但在使用它们时需要注意一些注意事项。
模型事件只从 Eloquent 模型中派发。这意味着,如果你使用 Illuminate\Support\Facades\DB
门面与模型的底层数据库数据进行交互,它的事件将不会被派发。
例如,让我们来看一个简单的例子,我们使用 Illuminate\Support\Facades\DB
门面来删除作者
use Illuminate\Support\Facades\DB; DB::table('authors') ->where('id', $author->id) ->delete();
运行上面的代码将按预期从数据库中删除作者。但 deleting
和 deleted
模型事件将不会被派发。因此,如果你在删除作者时为这些模型事件定义了任何监听器,它们将不会被运行。
同样,如果你使用 Eloquent 批量更新或删除模型,saved
、updated
、deleting
和 deleted
模型事件将不会被派发给受影响的模型。这是因为事件是从模型本身派发的。但在批量更新和删除时,模型实际上并没有从数据库中检索出来,所以事件不会被派发。
例如,假设我们使用以下代码删除作者
use App\Models\Author; Author::query()->whereKey($author->id)->delete();
由于 delete
方法直接在查询构建器上调用,因此 deleting
和 deleted
模型事件将不会被派发给该作者。
考虑替代方法
我喜欢在我的项目中使用模型事件。它们是解耦代码的好方法,也让我能够在对影响模型的代码没有太多控制权的情况下自动运行逻辑。例如,如果我在 Laravel Nova 中删除一个作者,我仍然可以运行一些逻辑来删除作者。
但是,重要的是要知道何时考虑使用不同的方法。
为了解释这一点,让我们来看一个我们可能想要避免使用模型事件的基本例子。扩展我们之前简单的博客应用程序示例,让我们想象一下,我们想要在每次创建新帖子时运行以下操作
- 计算帖子的阅读时间。
- 向 X/Twitter 发出 API 调用来分享帖子。
- 向平台上的每个订阅者发送通知。
因此,我们可能会创建三个单独的监听器(每个任务一个),它们在每次创建 App\Models\Post
的新实例时都会运行。
但现在让我们回过头来看看我们之前的一个测试
declare(strict_types=1); namespace Tests\Feature\Models; use App\Models\Author;use App\Models\Post;use Illuminate\Foundation\Testing\LazilyRefreshDatabase;use PHPUnit\Framework\Attributes\Test;use Tests\TestCase; final class AuthorTest extends TestCase{ use LazilyRefreshDatabase; #[Test] public function author_can_be_soft_deleted(): void { $author = Author::factory()->create(); $post = Post::factory()->for($author)->create(); $author->delete(); $this->assertSoftDeleted($author); $this->assertSoftDeleted($post); }}
如果我们运行上面的测试,当 App\Models\Post
模型通过其工厂创建时,它也会触发这三个操作。当然,计算阅读时间是一个次要的任务,所以它并不重要。但我们不想在测试期间尝试进行 API 调用或发送通知。这些都是意外的副作用。如果编写测试的开发人员没有意识到这些副作用,可能会更难跟踪为什么这些操作会发生。
我们还希望避免在监听器中编写任何特定于测试的逻辑,这会阻止这些操作在测试期间运行。这会使应用程序代码更复杂,更难维护。
这是你可能想要考虑更明确的方法而不是依赖于自动模型事件的一种情况。
一种方法可以是将你的 App\Models\Post
创建代码提取到服务或操作类中。例如,一个简单的服务类可能类似于
declare(strict_types=1); namespace App\Services; use App\DataTransferObjects\PostData;use App\Models\Post;use Illuminate\Support\Str; final readonly class PostService{ public function createPost(PostData $postData): void { $post = Post::create([ 'title' => $postData->title, 'content' => $postData->content, 'author_id' => $postData->authorId, 'read_time_in_seconds' => $this->calculateReadTime($postData->content), ]); $this->sendPostCreatedNotification($post); $this->publishToTwitter($post); } public function updatePost(Post $post, PostData $postData): void { $post->update([ 'title' => $postData->title, 'content' => $postData->content, 'read_time_in_seconds' => $this->calculateReadTime($postData->content), ]); } private function calculateReadTime(string $content): int { return (int) ceil( (Str::wordCount($content) / 265) * 60 ); } private function sendPostCreatedNotification(Post $post): void { // Send a notification to all subscribers... } private function publishToTwitter(Post $post): void { // Make an API call to Twitter... }}
在上面的类中,我们手动调用了计算阅读时间、发送通知和发布到 Twitter 的代码。这意味着我们对这些操作何时运行有更多控制权。我们还可以在测试中轻松模拟这些方法,以防止它们运行。我们仍然可以将这些操作排队(在这种情况下很可能需要这样做)。
因此,我们可以删除使用模型事件和监听器来执行这些操作。这意味着我们可以在应用程序代码中使用新的 App\Services\PostService
类,并在测试代码中安全地使用模型工厂。
这样做的好处是,它还可以使代码更易于理解。正如我之前简要提到的,使用事件和监听器的一个常见批评是它可能将业务逻辑隐藏在意外的地方。因此,如果新开发者加入团队,他们可能不知道某些操作在哪里或为什么发生,如果它们是由模型事件触发的。
但是,如果您仍然想使用事件和监听器来处理这种逻辑,您可以考虑使用更明确的方法。例如,您可以从服务类中调度一个事件,该事件会触发监听器。这样,您仍然可以使用事件和监听器的解耦优势,但可以更好地控制事件调度的时间。
例如,我们可以更新上面的 App\Services\PostService
示例中的 createPost
方法来调度一个事件
declare(strict_types=1); namespace App\Services; use App\DataTransferObjects\PostData;use App\Events\PostCreated;use App\Models\Post;use Illuminate\Support\Str; final readonly class PostService{ public function createPost(PostData $postData): void { $post = Post::create([ 'title' => $postData->title, 'content' => $postData->content, 'author_id' => $postData->authorId, 'read_time_in_seconds' => $this->calculateReadTime($postData->content), ]); PostCreated::dispatch($post); } // ... }
通过使用上述方法,我们仍然可以拥有独立的监听器来向 Twitter 发送 API 请求和发送通知。但我们可以更好地控制这些操作何时运行,这样在使用模型工厂时,它们就不会在测试中运行。
在决定使用这些方法中的哪一种时,没有绝对的规则。一切都要看什么最适合您、您的团队以及您正在构建的功能。但是,我倾向于遵循以下经验法则
- 如果监听器中的操作只是对模型进行一些微小的更改,请考虑使用模型事件。例如:生成 slug、计算阅读时间等。
- 如果操作将影响另一个模型(无论是自动创建、更新还是删除),那么请更明确,不要使用模型事件。
- 如果操作将与外部进程交互(API 调用、文件处理、触发通知、排队作业),那么请更明确,不要使用模型事件。
使用模型事件的优缺点
为了快速总结我们在本文中涵盖的内容,这里列出了使用模型事件的优缺点
优点
- 鼓励您解耦代码。
- 允许您自动触发操作,无论模型是在哪里创建/更新/删除的。例如,如果模型是在 Laravel Nova 中创建的,您可以触发业务逻辑。
- 您无需每次创建/更新/删除模型时都记住调度事件。
缺点
- 可能会导致意外的副作用。您可能希望创建/更新/删除模型而不会触发某些监听器,但这可能会导致意外的行为。这在编写测试时尤其成问题。
- 可能会将业务逻辑隐藏在难以追踪的意外地方。这会使您的代码流程更难理解。
结论
希望本文能为您提供有关模型事件是什么以及使用它们的各种方法的概述。它还应该向您展示如何测试模型事件代码,以及在使用它们时需要注意的一些问题。
您现在应该有足够的信心在 Laravel 应用程序中使用模型事件。