如何注册一个最后执行的模型事件监听器

最近在写一个插件:laravel-revaluation,它解决的问题举例说明就是将所有金额相关的字段以分存储,并提供更多便捷的方法方便以各种格式展现:

例如,$goods->price = 100; save 后存于数据库为:10000,当访问时:echo $goods->price; 得到 100。如果想以人民币格式展现:$goods->revaluated_price->asCurrency(); 得到 ¥100.00

内部实现其实就是重写 getAttribute 与 监听 saving 事件来完成属性值修改。但是今天又遇到各种坑,其实很多都是因为我们在事情监听时有很多监听器在处理,它们是按注册顺序执行的,也就是 setAttribute 可能会被重复调用,导致过早乘以100或者多次 * 100,总之坑了好久。

如果把事件监听放到最后一个执行就不会有这些问题了,于是我就看 Model 源码去了,找了半天发现其实在 Laravel 早些的版本好像是 13 年的时候吧,注册监听器是支持传优先级的,但是现在不支持,读遍了源代码最后在 bootIfNotBooted 方法看到了希望:

protected function bootIfNotBooted()
{
    if (! isset(static::$booted[static::class])) {
        static::$booted[static::class] = true;

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

        static::boot();

        $this->fireModelEvent('booted', false);
    }
}

我们在外部注册 Observer 时基本都在 ServiceProvider 的 boot 方法完成,所以除了在模型 boot 中注册监听器以外,就没有其它可以注册的地方了,那么,我就可以先注册一个 booted 监听器,然后在 booted 监听器内部注册 saving 监听器,这样我就可以保证我的这个 saving 监听是最后注册的了。

更多细节请参考源码:laravel-revaluation#HasRevaluableAttributes.php