巧用 Laravel 模型事件

file

Laravel模型事件容许你利用模型生命周期中的各个点,甚至能够防止发生保存或删除。 Laravel 模型事件文档 概述了如何使用事件类来引入这些事件,但本文旨在创建并补充关于设置事件和监听者的一些额外细节。php

事件概览

Eloquent 有许多事件,你能够将其添加到模型中并添加自定义功能。到目前为止,模型支持的事件以下:laravel

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

从官方文档中能够看到它们的工做原理,而且你能够跳到基本的Model类中,看看它们是如何工做的:git

当在数据库中一个模型 Model 被检索时,retrieved 事件将触发。 新模型第一次保存时,creatingcreated 事件将触发。 若是模型已经存在于数据库中而且调用了 save 方法,则 updating/ updated 事件将触发。 可是,在这两种状况下,saving / saved 事件都会触发。github

该文档提供了一个很好的概述,并解释了如何触发这些事件,但若是你是新手或不熟悉如何将事件监听器链接到这些自定义模型事件,请进一步阅读。数据库

注册事件

为了在模型中关联事件,首先须要使用 $dispatchesEvents 属性来注册事件对象,事件对象最终将经过 HasEvents::fireCustomModelEvent() 方法触发,该方法经过 fireModelEvent() 调用。fireCustomModelEvent() 方法以下所示:bash

/**
 * 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,而后退出操做。例如,你能够使用此hook进行一些检查并防止建立或删除用户。app

App\User 模型为例,配置模型事件:框架

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

你能够使用 php artisan make:event 命令来建立此事件,UserSaving 类的完整代码以下:ide

<?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 模型事件在保存期间触发,咱们须要为它注册一个监听者。咱们将在一秒钟内使用模型观察者,但我先来看下为单个事件配置事件(Event)和监听列表。

事件监听器就像任何其余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);
    }
}

绑定事件和监听者

上面只是给 app('log')->info($event->user); 添加了一个log调用,以便如今能够检查传递给侦听器的模型。为此,咱们须要在 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: "phpcasts",
     email: "model_event@phpcasts.org",
     updated_at: "2018-05-21 13:57:18",
     created_at: "2018-05-21 13:57:18",
     id: 2,
   }

若是你已正确注册了事件和侦听者,则应该在 storage/logs/laravel.log 文件中看到该模型的JSON表示形式:

[2018-05-17 13:57:18] local.INFO: {"name":"phpcasts","email":"model_event@phpcasts.org"}

请注意,此时模型没有 created_atupdated_at 属性。 若是你在模型上再次调用 save(),则日志将具备包含时间戳的新记录,由于 saving 事件会在新建立的记录和现有记录上触发:

>>> $u = factory(\App\User::class)->create();
=> App\User {#741
     name: "phpcasts",
     email: "model_event@phpcasts.org",
     updated_at: "2018-05-21 13:59:37",
     created_at: "2018-05-21 13:59:37",
     id: 3,
   }
>>> $u->save();
=> true
>>>

中止保存

一些模型事件容许你阻止继续操做。在咱们这个测试的例子中,假设咱们不但愿在 $user->name 属性中保存包含 adminroot 等名称的用户的模型,则能够这样作:

/**
 * Handle the event.
 *
 * @param  \App\Events\UserSaving $event
 * @return mixed
 */
public function handle(UserSaving $event)
{
    if (in_array($event->user->name, ['admin', 'root'])) {
        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)
    {
        //
    }
}

能够在服务提供者 AppServiceProviderboot() 方法中注册观察者 :

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

总结

事件/监听者 和 观察者均可以达到监听模型事件的做用,体会二者的使用。

更多

能够阅读 Laravel事件文档,以更多地了解事件和监听者如何在整个框架中工做。 Eloquent事件文档 是可用事件以及如何使用观察者的很好参考。 最后,我建议经过浏览 Illuminate\Database\Eloquent\Model 类来查找fireModelEvent() 方法调用的用法,以查看事件如何与模型以及将这些事件组合在一块儿的 HasEvent trait。

更多PHP知识,能够前往 PHPCasts

相关文章
相关标签/搜索