Laravel 模型事件入门
发布时间 作者 Paul Redmond
Laravel 模型事件允许您在模型生命周期的各个点进行操作,甚至可以阻止保存或删除操作。Laravel 模型 事件文档概述了如何使用事件类挂钩到这些事件,但这篇文章旨在在此基础上补充一些关于设置事件和监听器的额外细节。
事件概述
Eloquent 具有许多您可以挂钩并向您的模型添加自定义功能的事件。模型在编写时具有以下事件
- retrieved
- creating
- created
- updating
- updated
- saving
- saved
- deleting
- deleted
- restoring
- restored
从文档中可以了解到它们是如何工作的,您也可以跳转到基础 Model
类中查看它们的实现方式
当从数据库中检索现有模型时,
retrieved
事件将被触发。当新模型第一次被保存时,creating
和created
事件将被触发。如果模型已经存在于数据库中,并且调用了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", email: "[email protected]", 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_at
或 updated_at
属性。如果您再次对模型调用 save()
,日志将包含一个带有时间戳的新记录,因为 saving
事件在新创建的记录和现有记录上都会被触发
>>> $u = factory(\App\User::class)->create();=> App\User {#741 name: "Eloisa Hirthe", email: "[email protected]", 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 特征(将这些事件整合在一起)配合使用。