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

最近在写一个插件: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 协议》,转载必须注明作者和本文链接
本帖由系统于 3年前 自动加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 7

我是第一个点赞的

3年前 评论

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

3年前 评论

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

3年前 评论

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

3年前 评论
wujunze

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

3年前 评论

降龙十八赞~

3年前 评论

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