
在 Laravel 9+ 中使用 PHP 枚举时,若模型字段已通过 $casts 正确映射为枚举类型,资源类中应直接访问枚举的 value 属性(而非对象本身),否则会因序列化失败导致空数组 []。
在 laravel 9+ 中使用 php 枚举时,若模型字段已通过 `$casts` 正确映射为枚举类型,资源类中应直接访问枚举的 `value` 属性(而非对象本身),否则会因序列化失败导致空数组 `[]`。
当你将数据库中的字符串状态(如 'paid')通过 $casts = ['status' => OrderStatuses::class] 映射为 OrderStatuses 枚举后,$this->status 在模型层面确实是一个 OrderStatuses 实例(例如 OrderStatuses::Paid),而非你自定义的 PaidOrderStatus 对象——这一点至关重要。
⚠️ 注意:你当前的 protected function status(): Attribute 访问器存在严重逻辑冲突。它覆盖了 Laravel 默认的 cast 行为,将原始枚举值 OrderStatuses::Paid 进一步转换为 PaidOrderStatus 实例。这导致:
- 模型属性
->status不再是可序列化的枚举,而是一个自定义业务类; - Laravel 的 JSON 资源类(
JsonResource)无法自动序列化该对象,最终调用json_encode([]),输出空数组[]。
✅ 正确做法是 分离关注点:
- ✅ 使用
$casts确保数据库值 →OrderStatuses枚举(基础、可序列化、类型安全); - ❌ 移除
Attribute访问器,避免将枚举“升级”为不可序列化的业务对象; - ✅ 如需动态构建
OrderStatus实例(如调用canBeChanged()),应在服务层或控制器中按需创建,而非绑定到模型属性。
因此,在 OrdersResource 中,只需一行即可安全输出状态值:
'status' => $this->status->value, // 返回 'paid', 'pending' 等字符串
如果你仍需在资源中反映状态行为(例如是否可变更),可扩展为结构化数据:
'status' => [
'value' => $this->status->value,
'label' => match($this->status) {
OrderStatuses::New => '新订单',
OrderStatuses::Pending => '待处理',
OrderStatuses::Canceled => '已取消',
OrderStatuses::Paid => '已支付',
OrderStatuses::PaymentFailed => '支付失败',
},
'can_be_changed' => (new $this->status->createOrderStatus($this->resource))->canBeChanged(),
],? 提示:
$this->resource在JsonResource中指向原始模型实例,确保传入createOrderStatus()的是当前订单对象。也可将createOrderStatus方法改为静态方法以提升可测试性。
总结:永远优先信任 Laravel 的 cast 机制来处理枚举序列化;避免在模型访问器中引入不可序列化的对象转换。资源层只负责呈现数据,业务逻辑应下沉至领域类或服务中。


















