ThinkPHP 模型中使用 PHP 8.1+ enum 需手动定义访问器和修改器,因 TP6/TP7 不支持原生 enum 的 $casts;读取时用 tryFrom() 转枚举,写入时取 ->value;禁止在获取器返回中文文案以保类型安全;where 查询、验证规则、JSON 序列化需额外处理。

ThinkPHP 本身不原生支持 PHP 8.1+ 的 enum 类型,模型字段读写仍走 PDO 原始值(如字符串 'draft'),直接把 OrderStatus::Draft 赋给 $model->status 会丢失类型、存库失败或被强制转成空字符串。
ThinkPHP 模型里怎么用 PHP 8.1 enum 存数据库
必须手动加访问器(accessor)和修改器(mutator),不能只靠 $casts —— TP6/TP7 尚未内置对 PHP 原生 enum 的 cast 支持(Laravel 9.25+ 才有,TP 不同体系)。
- 字段值从数据库读出后是字符串,需用
MyEnum::from($value)转为枚举对象;非法值会抛ValueError,建议改用MyEnum::tryFrom($value) ?? MyEnum::Draft - 写入前要把枚举对象转回底层值:
$value->value(string或int) - 访问器名要匹配字段,比如字段叫
status,就定义getStatusAttr;返回枚举对象,供业务层安全使用 - 修改器名是
setStatusAttr,参数是$value(可能是枚举对象,也可能是字符串/整数),需做类型判断再归一化
protected function getStatusAttr($value)
{
return StatusEnum::tryFrom($value) ?? StatusEnum::Draft;
}
protected function setStatusAttr($value)
{
if ($value instanceof StatusEnum) {
return $value->value;
}
return StatusEnum::tryFrom($value)?->value ?? 'draft';
}
为什么不能直接在获取器里返回中文文案
获取器(getStatusTextAttr)返回文案是权宜之计,但和 PHP 原生 enum 的设计目标冲突:枚举本质是类型安全的“值”,不是展示层逻辑。一旦你把文案塞进获取器,就失去类型提示、match 分支检查、IDE 补全等全部收益。
- 返回文案后,
$article->status是字符串,$article->status === StatusEnum::Draft永远为 false -
match($article->status)会报错,因为match期待的是枚举对象,不是字符串 - 控制器或服务层想判断状态流转(比如从
Draft到Publish),只能靠魔法字符串比较,无法静态分析
STATUS_MAP 常量和 PHP enum 能共存吗
能,但没必要混用——PHP enum 本身已内建映射能力,StatusEnum::Draft->value 就是 'draft',StatusEnum::cases()(PHP 8.2+)可遍历所有 case,match 可穷尽转换文案。
立即学习“PHP免费学习笔记(深入)”;
- 旧项目有大量
STATUS_MAP数组,可先保留,但新逻辑应统一走enum+match - 不要在
enum里重复定义STATUS_MAP常量,那是退化用法;需要文案时,直接在enum内部加方法:
enum StatusEnum: string
{
case Draft = 'draft';
case Publish = 'publish';
public function label(): string
{
return match($this) {
self::Draft => '草稿',
self::Publish => '已发布',
};
}
}
调用 $status->label() 即可,比散落在模型里的数组更集中、更易维护。
容易被忽略的兼容性断点
PHP enum 在 ThinkPHP 中真正落地时,最常卡在三个地方:
-
where('status', StatusEnum::Draft)会失败 —— 查询条件不自动解包->value,必须显式写where('status', StatusEnum::Draft->value) - 模型验证规则(如
['in' => ['draft','publish']])仍需原始字符串,不能直接写['in' => StatusEnum::cases()](除非自己封装验证器) - JSON 序列化模型时,
status字段默认输出对象结构(如{"name":"Draft","value":"draft"}),而非预期字符串,需重写toJson或加toArray处理



















