如何注册一个最后执行的模型事件监听器
最近在写一个插件: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 协议》,转载必须注明作者和本文链接
我是第一个点赞的
"并提问便捷的方法以各种格式展现:"=>并提供便捷的方法以各种格式展现:joy:
@丁海军 哈哈,好,改改改
@itas 666
看样子是在 Eloquent 的保存方法之前使用了 DDD 的 Value Object 的方式进行转换
软妹币存储转化工具 好评!
降龙十八赞~
五年前就有这个了啊