Laravel中使用Trait复用折扣价格属性时,如何自动追加属性无需手动配置$appends?
我太懂这种“差一步完美”的感觉了——已经把重复的计算逻辑抽到Trait里了,结果还要每次手动在$appends里加属性名,总觉得还是有点啰嗦。别担心,咱们用Laravel的特性就能彻底解决这个问题,让Trait自动帮我们搞定属性追加的事,不用再手动维护$appends啦!
方法一:利用Trait的initialize方法 + mergeAppends(推荐)
Laravel的Trait有个很实用的特性:如果Trait里定义了initialize[TraitName]方法,当模型use这个Trait时,这个方法会在模型实例化时自动执行。再配合模型自带的mergeAppends方法,就能安全地把我们的折扣属性追加到模型的$appends数组里,还不会覆盖模型原来的追加属性。
修改你的HasDiscount Trait:
namespace App\Traits; trait HasDiscount { // 当模型use这个Trait时,自动执行这个初始化方法 public function initializeHasDiscount() { // 安全合并追加属性,模型原有的$appends会被保留,不会被覆盖 $this->mergeAppends(['price_with_discount']); } public function getPriceWithDiscountAttribute() { $discountAmount = floor(($this->price * $this->discount_percent) / 100); return $this->price - $discountAmount; } }
现在你的模型只需要简单use这个Trait就行,完全不用管$appends里的price_with_discount了!比如:
Product模型:
use Illuminate\Database\Eloquent\Model; use App\Traits\HasDiscount; class Product extends Model { use HasDiscount; // 保留模型自己需要的其他追加属性,Trait会自动合并折扣属性 protected $appends = ['unit']; }
Course模型:
use Illuminate\Database\Eloquent\Model; use App\Traits\HasDiscount; class Course extends Model { use HasDiscount; protected $appends = ['some_thing_else']; }
这样不管是从数据库查询模型实例,还是手动创建实例,price_with_discount都会自动被追加到模型的属性里,完全不用你手动维护$appends数组!
兼容旧版本Laravel(7及以下)
如果你的项目还在使用Laravel 7或更早的版本,没有mergeAppends方法,那我们可以手动做数组合并,同样在Trait的初始化方法里处理:
namespace App\Traits; trait HasDiscount { public function initializeHasDiscount() { // 合并数组并去重,避免重复添加同一个属性 $this->appends = array_unique( array_merge($this->appends, ['price_with_discount']) ); } public function getPriceWithDiscountAttribute() { $discountAmount = floor(($this->price * $this->discount_percent) / 100); return $this->price - $discountAmount; } }
为什么不用boot方法?
可能有人会想到用Trait的boot[TraitName]方法配合模型事件,比如retrieved事件,但这种方式只有在模型从数据库被取出时才会触发,如果你是手动新建模型实例(比如new Product()),属性就不会被追加。而initialize方法是在模型实例化时就执行,不管实例是来自数据库还是手动创建,都能生效,覆盖场景更全面。
这样调整之后,你的代码就真正做到了“一次编写,到处复用”——不管后续再增加多少需要折扣属性的模型,只要一行use HasDiscount;就搞定所有逻辑,既简洁又易维护!
内容来源于stack exchange

