
在 Laravel 9+ 中使用 PHP 8.1 枚举时,若在 Eloquent 资源(JsonResource)中直接访问已类型转换的 enum 字段(如 $this->status),默认会返回整个对象而非字符串值,导致 JSON 序列化为 [];应显式调用 ->value 获取底层标量值。
在 laravel 9+ 中使用 php 8.1 枚举时,若在 eloquent 资源(jsonresource)中直接访问已类型转换的 enum 字段(如 `$this->status`),默认会返回整个对象而非字符串值,导致 json 序列化为 `[]`;应显式调用 `->value` 获取底层标量值。
当你将 Eloquent 模型字段声明为 enum 并配置了 $casts(例如 'status' => OrderStatuses::class),Laravel 会在模型属性访问时自动将数据库中的字符串(如 'paid')转换为对应的枚举实例(如 OrderStatuses::Paid)。这本身是正确的,但枚举实例本身不可直接 JSON 序列化为字符串——PHP 默认将其视为对象,而 JsonResource 在序列化过程中对未实现 JsonSerializable 或 __toString() 的对象会输出空数组 []。
你当前的 Attribute 访问器:
protected function status(): Attribute
{
return new Attribute(
get: fn(string $value) => OrderStatuses::from($value)->createOrderStatus($this),
);
}
存在两个关键问题:
-
与
$casts冲突:$casts已完成string → OrderStatuses的转换,此访问器却进一步将OrderStatuses实例转为OrderStatus子类对象(如PaidOrderStatus),导致$this->status不再是枚举,而是自定义业务状态对象; -
缺乏可序列化支持:
PaidOrderStatus等类未实现JsonSerializable接口或__toString()方法,因此json_encode()对其结果为[]。
✅ 正确做法是:保持 $casts 定义,移除冗余的 Attribute 访问器,并在 Resource 中显式提取枚举值:
// ✅ 移除模型中的 status() Attribute 方法
// 只保留:
protected $casts = [
'status' => OrderStatuses::class,
];
然后在 OrdersResource 中直接使用 ->value:
class OrdersResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => (string)$this->id,
'type' => 'orders',
'attributes' => [
'status' => $this->status->value, // ← 关键修复:获取原始字符串值
'payment_type' => $this->payment_type,
'payment_transaction_no' => $this->payment_transaction_no,
'subtotal' => $this->subtotal,
'taxes' => $this->taxes,
'total' => $this->total,
'items' => OrderItemsResource::collection($this->whenLoaded('orderItems')),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
];
}
}
? 进阶建议(可选):若需在资源中同时暴露状态名称(如 "paid")和语义化标签(如 "Paid"),可在枚举中添加 label() 方法:
enum OrderStatuses: string
{
case New = 'new';
case Pending = 'pending';
case Canceled = 'canceled';
case Paid = 'paid';
case PaymentFailed = 'payment-failed';
public function label(): string
{
return match($this) {
self::New => 'New',
self::Pending => 'Pending',
self::Canceled => 'Canceled',
self::Paid => 'Paid',
self::PaymentFailed => 'Payment Failed',
};
}
}
并在资源中按需使用:
'status' => [
'value' => $this->status->value,
'label' => $this->status->label(),
],
⚠️ 注意事项:
- 不要同时使用
$casts和Attribute访问器处理同一字段,否则会导致类型覆盖和意外行为; - 若必须保留
OrderStatus对象逻辑(如状态流转校验),建议将其封装为独立服务(如OrderStatusService::canTransition(...)),而非通过模型访问器注入,以保持数据层与领域逻辑分离; - 所有自定义对象若需 JSON 序列化,务必实现
JsonSerializable接口或定义__toString()(仅当语义上明确代表单一字符串时)。
通过精简类型转换路径并显式提取 ->value,即可在保持代码清晰性的同时,确保 API 响应中枚举字段正确输出为预期字符串值。











