必须用 enum 替代魔法值,因硬编码状态易导致拼写错误、含义模糊、维护困难;推荐 php 8.1 原生 enum 或 myclabs/php-enum 库,并配合 eloquent cast 实现类型安全的自动转换。

在 Laravel 8.0 项目中用原始字符串或数字常量硬编码状态、类型、选项,会导致逻辑散落、含义模糊、修改风险高——比如把 'pending' 写成 'pendding',或把 2 当作「已发货」却实际对应「已取消」,这类错误上线后极难排查。
为什么必须用 Enum 替代魔法值
PHP 8.1 原生支持枚举(Enum),而 Laravel 8.0 虽未强制要求 PHP 8.1,但可通过 myclabs/php-enum 或自定义抽象基类实现类型安全的枚举。不用枚举时,Order::status === 'shipped' 这种判断既无法被 IDE 智能提示,也无法在编译期拦截拼写错误;一旦数据库字段值变更,所有散落在 Controller、Model、Job 中的字符串都要手动搜索替换,漏一处就埋下线上 Bug。
这一步不是锦上添花,而是堵住可预见的维护漏洞。
方法一:使用 myclabs/php-enum 库(兼容 PHP 7.4+)
第一步:执行 composer require myclabs/php-enum 安装依赖。
第二步:创建 app/Enums/OrderStatus.php,继承 MyCLabsEnumEnum:
php<br>namespace App\Enums;<br><br>use MyCLabs\Enum\Enum;<br><br>/** <br> * 订单状态枚举<br> * @method static OrderStatus PENDING()<br> * @method static OrderStatus CONFIRMED()<br> * @method static OrderStatus SHIPPED()<br> * @method static OrderStatus CANCELLED()<br> */<br>final class OrderStatus extends Enum<br>{<br> private const PENDING = 'pending';<br> private const CONFIRMED = 'confirmed';<br> private const SHIPPED = 'shipped';<br> private const CANCELLED = 'cancelled';<br>}
第三步:在模型中直接使用——$order->status = OrderStatus::SHIPPED();,赋值自动校验合法性;调用 $order->status->getValue() 获取底层字符串,$order->status->getKey() 获取常量名。
【注意】该库不支持 PHP 8.1+ 原生 Enum 的语法糖,且 getValue() 返回的是字符串而非 int,若数据库字段为 tinyint 类型,需额外映射层转换,否则会报错。
方法二:手写抽象基类 + PHP 8.1+ 原生 Enum(推荐新项目)
若项目已升级至 PHP 8.1,直接用原生枚举更轻量、IDE 支持更好、无需第三方依赖。
创建 app/Enums/OrderStatus.php:
php<br>namespace App\Enums;<br><br>enum OrderStatus: string<br>{<br> case PENDING = 'pending';<br> case CONFIRMED = 'confirmed';<br> case SHIPPED = 'shipped';<br> case CANCELLED = 'cancelled';<br><br> public function label(): string<br> {<br> return match($this) {<br> self::PENDING => '待确认',<br> self::CONFIRMED => '已确认',<br> self::SHIPPED => '已发货',<br> self::CANCELLED => '已取消',<br> };<br> }<br>}
在控制器中判断状态:if ($order->status === OrderStatus::SHIPPED) —— 此时 IDE 能自动补全、类型推导精准、PHPStan 静态分析可捕获非法赋值。
数据库写入时用 $order->status->value,读取时用 OrderStatus::from($dbValue),失败则抛出 ValueError,天然具备强约束。
方法三:Laravel 专属封装 —— 使用 Casts 处理枚举字段
让 Eloquent 模型字段自动完成枚举转换,避免每次手动调用 from() 和 ->value。
创建 app/Casts/OrderStatusCast.php:
php<br>namespace App\Casts;<br><br>use App\Enums\OrderStatus;<br>use Illuminate\Contracts\Database\Eloquent\CastsAttributes;<br><br>class OrderStatusCast implements CastsAttributes<br>{<br> public function get($model, $key, $value, $attributes)<br> {<br> return OrderStatus::tryFrom($value) ?? OrderStatus::PENDING;<br> }<br><br> public function set($model, $key, $value, $attributes)<br> {<br> return $value instanceof OrderStatus ? $value->value : $value;<br> }<br>}
在 app/Models/Order.php 中声明:
protected $casts = [<br> 'status' => OrderStatusCast::class,<br>];
此后对 $order->status 的读写完全透明:赋值 $order->status = OrderStatus::SHIPPED,读取 $order->status->label(),数据库存的是字符串 'shipped',代码里操作的是类型安全的枚举实例。
【关键前提】必须确保数据库字段类型为 varchar 或 enum,不能是 int;若历史字段为 tinyint,需先迁移为字符串类型再启用此 Cast,否则 tryFrom() 会因类型不匹配静默失败。











