Laravel 模型事件入门

发布时间 作者

Getting Started with Laravel Model Events image

Laravel 模型事件允许您在模型生命周期的各个点进行操作,甚至可以阻止保存或删除操作。Laravel 模型 事件文档概述了如何使用事件类挂钩到这些事件,但这篇文章旨在在此基础上补充一些关于设置事件和监听器的额外细节。

事件概述

Eloquent 具有许多您可以挂钩并向您的模型添加自定义功能的事件。模型在编写时具有以下事件

  • retrieved
  • creating
  • created
  • updating
  • updated
  • saving
  • saved
  • deleting
  • deleted
  • restoring
  • restored

从文档中可以了解到它们是如何工作的,您也可以跳转到基础 Model 类中查看它们的实现方式

当从数据库中检索现有模型时,retrieved 事件将被触发。当新模型第一次被保存时,creatingcreated 事件将被触发。如果模型已经存在于数据库中,并且调用了 save 方法,则 updating / updated 事件将被触发。但是,在这两种情况下,saving / saved 事件都会被触发。

文档对事件提供了一个很好的概述,并解释了如何挂钩到这些事件,但如果您是新手或不熟悉如何将事件监听器挂钩到这些自定义模型事件,请继续阅读。

注册事件

为了挂钩到模型上的事件,您需要做的第一件事是使用 $dispatchesEvents 属性来注册事件对象,这些事件对象最终将通过 HasEvents::fireCustomModelEvent() 方法触发,该方法通过 fireModelEvent() 方法调用。fireCustomModelEvent() 方法在编写时如下所示

/**
* Fire a custom model event for the given event.
*
* @param string $event
* @param string $method
* @return mixed|null
*/
protected function fireCustomModelEvent($event, $method)
{
if (! isset($this->dispatchesEvents[$event])) {
return;
}
 
$result = static::$dispatcher->$method(new $this->dispatchesEvents[$event]($this));
 
if (! is_null($result)) {
return $result;
}
}

某些事件(例如 delete)将检查事件是否返回 false,然后退出操作。例如,您可以使用此挂钩进行一些检查并阻止用户创建或删除。

App\User 模型为例,以下是配置模型事件的方式

protected $dispatchesEvents = [
'saving' => \App\Events\UserSaving::class,
];

您可以使用 artisan make:event 命令为您创建此事件,但本质上您最终将得到以下结果

<?php
 
namespace App\Events;
 
use App\User;
use Illuminate\Queue\SerializesModels;
 
class UserSaving
{
use SerializesModels;
 
public $user;
 
/**
* Create a new event instance.
*
* @param \App\User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}
}

我们的事件类提供了一个公共的 $user 属性,以便您在 saving 事件期间访问 User 模型实例。

要使它正常工作,下一步是为事件设置一个实际的监听器。当 User 模型触发 saving 事件时,将调用监听器,从而允许我们在事件期间访问模型。

创建事件监听器

现在我们的自定义 User 模型事件在 saving 期间触发,我们需要为它注册一个监听器。我们将在下一节讨论使用模型观察者,但我想引导您完成为单个事件配置事件和监听器的过程。

事件监听器与其他 Laravel 事件监听器类似,handle() 方法将接受 App\Events\UserSaving 事件类的实例。

您可以手动创建它,也可以使用 php artisan make:listener 命令。无论您使用哪种方式创建它,监听器类都将如下所示

<?php
 
namespace App\Listeners;
 
use App\Events\UserSaving as UserSavingEvent;
 
class UserSaving
{
/**
* Handle the event.
*
* @param \App\Events\UserSavingEvent $event
* @return mixed
*/
public function handle(UserSavingEvent $event)
{
app('log')->info($event->user);
}
}

我只添加了一个对记录器的调用,以便我现在可以检查传递给监听器的模型。要使它正常工作,我们需要在 EventServiceProvider::$listen 属性中注册监听器

<?php
 
namespace App\Providers;
 
use Illuminate\Support\Facades\Event;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
 
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
\App\Events\UserSaving::class => [
\App\Listeners\UserSaving::class,
],
];
 
// ...
}

现在,当模型调度事件时,我们的监听器将被注册,并在 saving 事件期间被调用。

尝试事件处理程序

我们可以通过一个快速的 tinker 会话来试用我们的事件监听器代码

php artisan tinker
>>> factory(\App\User::class)->create();
=> App\User {#794
name: "Aiden Cremin",
updated_at: "2018-03-15 03:57:18",
created_at: "2018-03-15 03:57:18",
id: 2,
}

如果您已正确注册事件和监听器,您应该在 laravel.log 文件中看到模型的 JSON 表示

[2018-03-15 03:57:18] local.INFO: {"name":"Aiden Cremin","email":"[email protected]"}

请注意,此时模型没有 created_atupdated_at 属性。如果您再次对模型调用 save(),日志将包含一个带有时间戳的新记录,因为 saving 事件在新创建的记录和现有记录上都会被触发

>>> $u = factory(\App\User::class)->create();
=> App\User {#741
name: "Eloisa Hirthe",
updated_at: "2018-03-15 03:59:37",
created_at: "2018-03-15 03:59:37",
id: 3,
}
>>> $u->save();
=> true
>>>

阻止保存

某些模型事件允许您阻止操作继续进行。在我们荒谬的例子中,假设我们不想允许保存任何 $user->name 属性中包含 Paul 的用户

/**
* Handle the event.
*
* @param \App\Events\UserSaving $event
* @return mixed
*/
public function handle(UserSaving $event)
{
if (stripos($event->user->name, 'paul') !== false) {
return false;
}
}

在基于 Eloquent 的 Model::save() 方法中,有这个事件检查,它将根据此事件处理程序的结果阻止保存发生

public function save(array $options = [])
{
$query = $this->newQueryWithoutScopes();
 
// If the "saving" event returns false we'll bail out of the save and return
// false, indicating that the save failed. This provides a chance for any
// listeners to cancel save operations if validations fail or whatever.
if ($this->fireModelEvent('saving') === false) {
return false;
}

save() 方法是您自定义事件如何访问模型的生命周期并被动地执行一些操作(如记录数据或调度作业)的一个很好的例子。

使用观察者

如果您正在监听多个事件,您可能会发现使用观察者类在一个类中对多个事件进行分组会更加方便。以下来自 Eloquent 事件文档 的示例

<?php
 
namespace App\Observers;
 
use App\User;
 
class UserObserver
{
/**
* Listen to the User created event.
*
* @param \App\User $user
* @return void
*/
public function created(User $user)
{
//
}
 
/**
* Listen to the User deleting event.
*
* @param \App\User $user
* @return void
*/
public function deleting(User $user)
{
//
}
}

您可以在服务提供者的 boot() 方法中注册观察者。一个很好的添加这些的地方是 AppServiceProvider

/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::observe(UserObserver::class);
}

了解更多

我建议您阅读出色的 Laravel 事件文档,以了解有关事件和监听器如何在整个框架中工作的更多信息。 Eloquent 事件文档 是有关可用事件以及如何使用观察者的良好参考。最后,我建议您浏览 Illuminate\Database\Eloquent\Model 类,找到 fireModelEvent() 方法调用的用法,以了解事件如何与模型以及 HasEvents 特征(将这些事件整合在一起)配合使用。

Paul Redmond photo

Laravel 新闻的撰稿人。全栈 Web 开发人员和作家。

Cube

Laravel 新闻稿

加入 40,000 多名其他开发者,不错过任何新的技巧、教程等等。

Laravel Forge logo

Laravel Forge

轻松创建和管理您的服务器,并在几秒钟内部署您的 Laravel 应用程序。

Laravel Forge
Tinkerwell logo

Tinkerwell

Laravel 开发人员必备的代码运行器。使用 AI、自动完成和对本地和生产环境的即时反馈进行调试。

Tinkerwell
No Compromises logo

无妥协

Joel 和 Aaron,来自 No Compromises 播客的两位经验丰富的开发人员,现在可以为您的 Laravel 项目提供服务。 ⬧ 固定费率为每月 7500 美元。 ⬧ 无需冗长的销售流程。 ⬧ 无需签订合同。 ⬧ 100% 退款保证。

无妥协
Kirschbaum logo

Kirschbaum

提供创新和稳定性,确保您的 Web 应用程序取得成功。

Kirschbaum
Shift logo

Shift

运行旧版 Laravel?即时、自动化的 Laravel 升级和代码现代化,让您的应用程序保持新鲜感。

Shift
Bacancy logo

Bacancy

只需每月 2500 美元,即可为您的项目配备一位经验丰富的 Laravel 开发人员,拥有 4-6 年的经验。获得 160 小时的专业知识和无风险的 15 天试用期。立即安排通话!

Bacancy
Lucky Media logo

Lucky Media

现在就试试 Lucky - Laravel 开发的理想选择,拥有十多年的经验!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel 电子商务

Laravel 的电子商务。一个开源包,将现代无头电子商务功能的力量带入 Laravel。

Lunar: Laravel 电子商务
LaraJobs logo

LaraJobs

官方 Laravel 工作板

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit:Laravel SaaS 启动工具包

SaaSykit 是一个 Laravel SaaS 启动工具包,它包含运行现代 SaaS 所需的所有功能。付款、美观结账、管理面板、用户仪表板、身份验证、现成组件、统计数据、博客、文档等等。

SaaSykit:Laravel SaaS 启动工具包
Rector logo

Rector

您无缝升级 Laravel、降低成本和加速创新的合作伙伴,为成功企业服务。

Rector
MongoDB logo

MongoDB

使用 MongoDB 和 Laravel 的强大集成增强您的 PHP 应用程序,使开发人员能够轻松高效地构建应用程序。支持事务性、搜索、分析和移动用例,同时使用熟悉的 Eloquent API。探索 MongoDB 的灵活、现代数据库如何改变您的 Laravel 应用程序。

MongoDB
Maska is a Simple Zero-dependency Input Mask Library image

Maska 是一个简单的零依赖输入掩码库。

阅读文章
Add Swagger UI to Your Laravel Application image

在您的 Laravel 应用程序中添加 Swagger UI

阅读文章
Assert the Exact JSON Structure of a Response in Laravel 11.19 image

在 Laravel 11.19 中断言响应的精确 JSON 结构

阅读文章
Build SSH Apps with PHP and Laravel Prompts image

使用 PHP 和 Laravel 提示构建 SSH 应用程序

阅读文章
Building fast, fuzzy site search with Laravel and Typesense image

使用 Laravel 和 Typesense 构建快速、模糊的网站搜索

阅读文章
Add Comments to your Laravel Application with the Commenter Package image

使用 Commenter 包为您的 Laravel 应用程序添加评论

阅读文章