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

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

本帖由系统于 1年前 自动加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 7

我是第一个点赞的

1年前
丁海军

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

1年前
overtrue

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

1年前
overtrue

@itas 666

1年前

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

1年前
wujunze

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

1年前

降龙十八赞~

1年前

  • 请注意单词拼写,以及中英文排版,参考此页
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
  • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
  • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
  • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
  请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!