【Laravel-海贼王系列】第十五章,Model Observer 解析

1,943

简介

可以替代数据库的触发器功能。

Observer 的创建

观察者的功能来自与继承的 Model 对象的 trait HasEvents 特质。

本章我们使用 Laravel 默认的 User 模型来进行说明。

创建一个观察者类

注册 Observer

通过 provider 来注册 Observer

关于 provider 加载机制可以移步【Laravel-海贼王系列】第八章, Provider 功能解析

观察过程解析

User::observe() 方法来自 Illuminate\Database\Eloquent\Concerns\HasEvents;

上面介绍过了,这个特质类是关键,我也主要围绕这个类来分析。

public static function observe($classes)
{
    // "指向 User 对象"
    $instance = new static;

    // "支持一次传入数组或者字符串的型式统一包装成数组"
    // "这里可以看出 observe() 方法支持一次观察多个类"
    foreach (Arr::wrap($classes) as $class) {
        
        $instance->registerObserver($class);
    }
}

展开 $instance->registerObserver($class);

// "刚才的传入 $class = 'App\UserObserver'"
protected function registerObserver($class)
{
    $className = is_string($class) ? $class : get_class($class);

    // "这里就是获取指定的事件名称"
    $this->getObservableEvents()  = array_merge(
        [
            'retrieved', 'creating', 'created', 'updating', 'updated',
            'saving', 'saved', 'restoring', 'restored',
            'deleting', 'deleted', 'forceDeleted',
        ],
        $this->observables, // "这个是提供给用户自己定义的方法"
    );

    foreach ($this->getObservableEvents() as $event) {

        // "还记得 'App\UserObserver' 中定义的方法吗!"
    
        if (method_exists($class, $event)) {
            static::registerModelEvent($event, $className.'@'.$event);
        }
    }
}

注册 Observer 中的事件

上面循环了所有的提前指定事件,然后逐个检查观察者并进行注册。

继续分析 static::registerModelEvent($event, $className.'@'.$event);

protected static function registerModelEvent($event, $callback)
{
    if (isset(static::$dispatcher)) {
        $name = static::class;

        static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback);
    }
}

statci::$dispatcherlluminate\Events\Dispatcher 对象

所以其实这里迭代调用了,可以看到就是绑定一个个事件,同时将事件 指向UserObserver@functionName的对应方法中。

static::$dispatcher->listen("eloquent.retrieved: App\User", 'App\UserObserver@retrieved');
static::$dispatcher->listen("eloquent.creating: App\User", 'App\UserObserver@creating');
static::$dispatcher->listen("eloquent.created: App\User", 'App\UserObserver@created');
..... 省略类似代码
static::$dispatcher->listen("eloquent.forceDeleted: App\User", 'App\UserObserver@forceDeleted');

关于事件的绑定以及触发机制请移步【Laravel-海贼王系列】第九章, Events 功能解析

经历过上面种种绑定我们已经绑定了需要的事件,并且监听者就是 UserObserver 对象。

接下来就是如何触发的问题了。

触发 Observer 监听的事件

我们来看常用的生成用户的方法。

User::create(
    [
        'name'     => "big pig's feet",
        'email'    => 'hello@world.com',
        'password' => password_hash('123456', PASSWORD_BCRYPT, ['cost' => 12,]),
    ]
);

⚠️这里 create 方法在 Model 中并不存在,会调用魔术方法 __call()

我们本章只关心观察者,查询构建器后续在涉及!直接看最终调用方法

位于 ModelperformInsert 方法

protected function performInsert(Builder $query)
{
    if ($this->fireModelEvent('creating') === false) {
        return false;
    }
    
    if ($this->usesTimestamps()) {
        $this->updateTimestamps();
    }

    $attributes = $this->getAttributes();

    if ($this->getIncrementing()) {
        $this->insertAndSetId($query, $attributes);
    }else {
        if (empty($attributes)) {
            return true;
        }

        $query->insert($attributes);
    }
    
    $this->exists = true;

    $this->wasRecentlyCreated = true;

    $this->fireModelEvent('created', false);

    return true;
}

这里我们不详解如何执行事件的代码,我们只关心执行了什么!

执行逻辑分析:

首先触发模型之前绑定的 creating 方法。

调用最后插入数据的逻辑。

设置一些放重复的属性

调用绑定的 created 事件。

到了这里可以知道官方给出的许多事件以及执行顺序的原因!

结语

LaravelModel 功能非常多,本章只是

简单的介绍了其中一种功能“观察者”,还有很多方法没有分析

但是基本原理我们只要知道一种其他也都是一样。

最后总结一下

Model 结合 laravel 事件来完成观察者事件的监听和触发。

Provider 提供了观察者在启动过程的加载服务。