eloquent attribute actions 是 laravel 11+ 社区对 $casts 结合 casts\attribute 实现属性读写拦截与副作用触发的俗称,本质是在 setter 中手动编写状态变更逻辑(如发通知、更新关联字段),需用 isdirty() 判断真实变更、返回 $value 防止值丢失,并避免在 set 中调用 save() 引发递归。

什么是 Eloquent Attribute Actions(Laravel 11+)
这不是 Laravel 官方术语,而是社区对 Laravel 11 引入的 $casts + Attribute 类组合实现「属性读写拦截 + 副作用触发」的一种通俗叫法。它本质是用 Illuminate\Database\Eloquent\Casts\Attribute 封装 getter/setter,并在 setter 中触发状态变更逻辑(比如发通知、更新关联字段、校验前置条件)。别被“Actions”误导——Eloquent 没有内置的 action 机制,全靠你手动在 setter 里写。
怎么用 Attribute 实现状态转换副作用
典型场景:订单从 pending → confirmed 时,自动记录确认时间、通知用户、扣减库存。不能只改字段值,必须带副作用。
正确做法是把状态字段设为 cast 成 Attribute,并在 set 闭包里做判断和动作:
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Facades\Notification;
protected function status(): Attribute
{
return Attribute::make(
get: fn ($value) => $value,
set: function ($value) {
// 只在状态真正变更时触发副作用
if ($this->isDirty('status') && $this->status !== $value) {
$old = $this->status;
$new = $value;
if ($old === 'pending' && $new === 'confirmed') {
$this->confirmed_at = now();
Notification::route('mail', $this->user->email)
->notify(new OrderConfirmed($this));
$this->decrementStock(); // 自定义方法
}
return $value;
}
return $value;
}
);
}
- 必须用
$this->isDirty('status')判断是否真变了,否则批量赋值或重复 save 会反复触发 - 不要在
set里调$this->save()—— 这会引发递归保存和事务异常 - 副作用逻辑建议抽成私有方法(如
decrementStock()),便于测试和复用 - 如果副作用失败(如通知发不出),应抛出异常中断保存,否则状态不一致
为什么不能直接在 model events(如 saved)里做
因为 saved 是全局钩子,无法区分「是哪个字段变导致的保存」,也无法拿到旧值。而 Attribute 的 set 是精准拦截,天然知道「谁被设了、设成啥、之前是啥」。
-
saved事件里查$this->getChanges()不可靠:批量更新、updateQuietly()会绕过,且无法回溯原始意图 - 状态机类库(如
spatie/laravel-model-status)适合复杂多状态流转,但小项目用Attribute更轻量、无依赖 - 如果副作用涉及外部 API 或队列,务必在
set中 dispatch job 而非同步执行,避免阻塞数据库事务
常见踩坑:值被覆盖、副作用重复执行、类型不一致
Attribute 的 set 返回值会直接写入模型属性,若没返回或返回错类型,字段值就丢了。
- 忘记
return $value→ 字段变成null,且不会报错 - 在
set里修改其他字段(如$this->updated_at = now())但没声明为fillable或casts→ 被 mass assignment 过滤掉 - 前端传字符串
"1",但数据库是 tinyint,set里没 cast 类型 → 后续比较(如$new === 'confirmed')失败 - 用
fill()或构造函数初始化时,Attribute 的set不触发 —— 它只响应「赋值操作」,不响应「属性初始化」
真正难的不是写代码,是厘清副作用该在哪一层触发:是每次赋值都检查?还是只在特定业务方法中显式调用?Attribute 提供了能力,但边界得你自己划清楚。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











