laravel 11 中原生 php 枚举需通过自定义 cast 类实现双向转换,$casts 不支持直接传枚举类;必须实现 castsattributes 接口,用 tryfrom 安全处理 null 和非法值,并推荐使用 casts() 方法注册。

在 Laravel 11 中,原生 PHP 枚举(enum)不能直接靠 $casts 自动识别和转换——它需要你显式提供一个 Cast 类来桥接数据库值与枚举实例。访问器(accessor)本身不参与类型转换,只负责读取时的格式化;真正完成“数据库值 ↔ 枚举对象”双向转换的是自定义 Cast 类。
必须用 Cast 类,不能只靠访问器或 $casts 字符串
Laravel 不会自动把 public enum Status: string 当作可转换字段。即使你在模型里写:
protected $casts = ['status' => Status::class];
这行不通:Laravel 11 的 $casts 数组只接受字符串(如 'string'、'boolean')或实现了 CastsAttributes 接口的类名,而 PHP 原生枚举类不实现该接口。
正确做法是:
- 运行
php artisan make:cast StatusCast创建 Cast 类 - 让该类实现
Illuminate\Contracts\Database\Eloquent\CastsAttributes - 在
get()中用Status::tryFrom($value)安全构造枚举(避免抛ValueError) - 在
set()中对null、空字符串、非法值做预处理,返回底层值(如$value?->value或默认值)
Cast 类中必须处理非法值和 NULL
数据库里可能存着旧数据、空字符串、被删掉的枚举项,或字段允许 NULL。如果直接用 Status::from(),遇到不匹配值就会崩溃。
推荐写法(使用 tryFrom):
public function get($model, string $key, $value, array $attributes)
{
return $value === null ? null : Status::tryFrom($value);
}
public function set($model, string $key, $value, array $attributes)
{
if ($value instanceof Status) {
return $value->value;
}
if (is_string($value) && !empty($value)) {
$enum = Status::tryFrom($value);
return $enum?->value ?? Status::Draft->value;
}
return Status::Draft->value;
}
这样既防错,又保证写入数据库的是合法字符串(或整数),不会污染数据。
模型中注册 Cast 的两种方式(Laravel 11 支持)
方式一:传统 $casts 数组(清晰、易调试)
protected $casts = [
'status' => StatusCast::class,
];
方式二:链式声明(Laravel 11+ 推荐,更语义化)
protected function casts(): array
{
return [
'status' => StatusCast::class,
];
}
两者效果一致,后者支持 IDE 更好提示,也方便后续加逻辑(比如根据环境动态返回不同 Cast)。
访问器只做展示层处理,别让它承担转换职责
比如你想在 Blade 中显示“已发布”而不是 Status::Published,可以加一个访问器:
public function getStatusLabelAttribute(): string
{
return match($this->status) {
Status::Draft => '草稿',
Status::Published => '已发布',
Status::Archived => '已归档',
default => '未知',
};
}
注意:$this->status 此时已是 Status 实例(因为 Cast 已在读取时完成转换),所以访问器拿到的是类型安全的对象,不是原始字符串。访问器不干预数据库读写,只影响 $model->status_label 这样的调用。











