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

最近在写一个插件: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

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由系统于 6年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 8

我是第一个点赞的

6年前 评论

"并提问便捷的方法以各种格式展现:"=>并提供便捷的方法以各种格式展现:joy:

6年前 评论

@丁海军 哈哈,好,改改改

6年前 评论

看样子是在 Eloquent 的保存方法之前使用了 DDD 的 Value Object 的方式进行转换

6年前 评论

软妹币存储转化工具 好评!

6年前 评论

降龙十八赞~

6年前 评论

五年前就有这个了啊

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!