不再建议使用该库,尤其是对于新项目。 PHP 8.1 原生支持枚举。
参见#332。
Laravel 的简单、可扩展且强大的枚举实现。
枚举键值对作为类常量
功能齐全的方法套件
枚举实例化
标记/按位枚举
类型提示
属性铸造
枚举工匠生成器
将枚举键或值作为输入参数传递的验证规则
本地化支持
可通过宏扩展
由本·桑普森创建
指导
安装
迁移到本机 PHP 枚举
枚举库
基本用法
枚举定义
实例化
实例属性
实例铸造
实例平等
类型提示
标记/按位枚举
属性铸造
迁移
验证
本土化
定制描述
自定义类描述
自定义值描述
扩展枚举基类
Laravel Nova 集成
PHPStan 集成
工匠命令列表
枚举类参考
存根
您正在阅读6.x
的文档。
如果您使用Laravel 8,请参阅4.x
的文档。
如果您使用Laravel 7,请参阅2.x
的文档。
如果您使用Laravel 6或更低版本,请参阅1.x
的文档。
有关如何升级到最新版本的信息,请参阅升级指南。
我写了一篇关于使用 laravel-enum 的博客文章:https://sampo.co.uk/blog/using-enums-in-laravel
需要 PHP 8 和 Laravel 9 或 10。
作曲家需要 bensampo/laravel-enum
PHP 8.1 原生支持枚举。您可以使用以下步骤将BenSampoEnumEnum
的使用迁移到本机 PHP 枚举。
确保您满足以下要求:
PHP 8.1 或更高版本
Laravel 10 或更高版本
Rector 0.17 或更高版本,您的rector.php
包含所有相关文件
该库的最新版本
根据项目的大小,您可以选择一次迁移所有枚举,或者一次仅迁移几个或一个枚举。
一次转换所有枚举: php artisan enum:to-native
传递枚举的完全限定类名以限制转换: php artisan enum:to-native "AppEnumsUserType"
如果在 Laravel 的引导阶段使用任何枚举,则这是必要的,其用法的转换会干扰 Larastan 并阻止 Rector 的第二次运行。
检查并验证代码更改以发现遗漏的边缘情况:
查看未实现
Enum::coerce()
:如果仅传递值,则可以将其替换为tryFrom()
。如果还可以传递键或实例,您可能需要额外的逻辑来涵盖这一点。
Enum::$description
和Enum::getDescription()
:实现替代方案。
处理BenSampoEnumExceptionsInvalidEnumKeyException
或BenSampoEnumExceptionsInvalidEnumMemberException
的 try/catch 块。要么捕获本机枚举抛出的ValueError
,要么切换到使用tryFrom()
并处理null
。
转换所有枚举后,您可以删除对此库的依赖。
从常用的、社区贡献的枚举列表中浏览和下载。
枚举库 →
您可以使用以下 Artisan 命令生成新的枚举类:
php artisan make:enum 用户类型
现在,您只需添加枚举可以具有的可能值作为常量。
<?php 声明(strict_types=1);命名空间 AppEnums;use BenSampoEnumEnum;final class UserType extends Enum {const 管理员 = 0;const 主持人 = 1;const 订阅者 = 2;const 超级管理员 = 3; }
就是这样!请注意,由于枚举值被定义为普通常量,因此您可以像任何其他类常量一样简单地访问它们。
UserType::Administrator // 值为 0
实例化枚举以便在函数之间传递它们并具有类型提示的好处非常有用。
此外,不可能用无效值实例化枚举,因此您可以确定传递的值始终有效。
为了方便起见,枚举可以通过多种方式实例化:
// 标准新 PHP 类,将所需的枚举值作为参数传递$enumInstance = new UserType(UserType::Administrator);// 与构造函数相同,按值实例化$enumInstance = UserType::fromValue(UserType::Administrator) ;// 使用枚举键而不是其值$enumInstance = UserType::fromKey('Administrator');// 利用 __callStatic 将键名作为方法静态调用magic$enumInstance = UserType::Administrator();// 尝试使用给定的键或值实例化一个新的 Enum。如果 Enum 无法实例化,则返回 null。$enumInstance = UserType::coerce($someValue);
如果您希望 IDE 自动完成静态实例化助手,您可以通过 artisan 命令生成 PHPDoc 注释。
默认情况下, app/Enums
中的所有枚举都将被注释(您可以通过将路径传递给--folder
来更改文件夹)。
php artisan 枚举:注释
您可以通过指定类名来注释单个类。
php artisan enum:注释“AppEnumsUserType”
拥有枚举实例后,您可以将key
、 value
和description
作为属性进行访问。
$userType = UserType::fromValue(UserType::SuperAdministrator);$userType->key; // 超级管理员$userType->value; // 3$userType->描述; // 超级管理员
如果您将枚举实例传递到刀片视图,这尤其有用。
枚举实例可以在实现__toString()
魔术方法时转换为字符串。
这也意味着它们可以在刀片视图中得到回响,例如。
$userType = UserType::fromValue(UserType::SuperAdministrator); (字符串) $userType // '3'
您可以通过将实例传递给is
方法来检查实例与任何值的相等性。为了方便起见,还有一个isNot
方法,它与is
方法完全相反。
$admin = UserType::Administrator();$admin->is(UserType::Administrator); // true$admin->is($admin); // true$admin->is(UserType::Administrator()); // true$admin->is(UserType::Moderator); // false$admin->is(UserType::Moderator()); // false$admin->is('随机值'); // 错误的
您还可以使用in
方法检查实例的值是否与可能值的数组匹配,并使用notIn
检查实例值是否不在值数组中。也可以检查可迭代对象。
$admin = UserType::Administrator();$admin->in([UserType::Moderator, UserType::Administrator]); // true$admin->in([UserType::Moderator(), UserType::Administrator()]); // true$admin->in([UserType::Moderator, UserType::Subscriber]); // false$admin->in(['随机值']); // false$admin->notIn([UserType::Moderator, UserType::Administrator]); // false$admin->notIn([UserType::Moderator(), UserType::Administrator()]); // false$admin->notIn([UserType::Moderator, UserType::Subscriber]); // true$admin->notIn(['随机值']); // 真的
实例化的枚举不是单例,而是每次都会创建一个新对象。因此,不同枚举实例的严格比较===
将始终返回false
,无论值如何。相反,松散比较==
将取决于值。
$admin = UserType::Administrator();$admin === UserType::Administrator(); // falseUserType::Administrator() === UserType::Administrator(); // false$admin === UserType::Moderator(); // false$admin === $admin; // true$admin == UserType::Administrator(); // true$admin == UserType::Administrator; // true$admin == UserType::Moderator(); // false$admin == UserType::Moderator; // 错误的
枚举实例的好处之一是它使您能够使用类型提示,如下所示。
函数 canPerformAction(UserType $userType) {if ($userType->is(UserType::SuperAdministrator)) {返回 true; }返回错误; }$userType1 = UserType::fromValue(UserType::SuperAdministrator);$userType2 = UserType::fromValue(UserType::Moderator);canPerformAction($userType1); // 返回 truecanPerformAction($userType2); // 返回假
标准枚举一次表示一个值,但带标记或按位枚举能够同时表示多个值。这使得它们非常适合当您想要表达一组有限选项的多个选择时。一个很好的例子是用户权限,其中可能的权限数量有限,但用户可以没有、部分或全部权限。
您可以使用以下 artisan 命令创建带标记的枚举:
php artisan make:enum UserPermissions --flagged
定义值时必须使用 2 的幂,最简单的方法是使用左移<<
运算符,如下所示:
最终类 UserPermissions 扩展了 FlaggedEnum {const ReadComments = 1 << 0;const WriteComments = 1 << 1;const EditComments = 1 << 2;const DeleteComments = 1 << 3;// 下一个将是 `1 << 4` 等等。 ..}
您可以使用按位或|
设置代表一组给定值的快捷值。
最终类 UserPermissions 扩展了 FlaggedEnum {const ReadComments = 1 << 0;const WriteComments = 1 << 1;const EditComments = 1 << 2;const DeleteComments = 1 << 3;// 快捷方式 const Member = self::ReadComments | self::写评论; // 读取和写入。const Moderator = self::Member | self::编辑评论; // 会员拥有的所有权限,加上 Edit.const Admin = self::Moderator | self::删除评论; // 版主拥有的所有权限,加上删除。}
有几种方法可以实例化带标记的枚举:
// 标准的新 PHP 类,将所需的枚举值作为值数组或枚举实例数组传递$permissions = new UserPermissions([UserPermissions::ReadComments, UserPermissions::EditComments]);$permissions = new UserPermissions([UserPermissions: :ReadComments(), UserPermissions::EditComments()]);// 静态标志方法,再次将所需的枚举值作为值数组或数组传递枚举实例$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::EditComments]);$permissions = UserPermissions::flags([UserPermissions::ReadComments(), UserPermissions::EditComments()]);
属性转换的工作方式与单值枚举相同。
带标记的枚举可以不包含任何值。每个标记的枚举都有一个预定义的常量None
,它与0
相当。
UserPermissions::flags([])->value === UserPermissions::None; // 真的
除了标准枚举方法之外,还有一系列可用于标记枚举的有用方法。
注意:在传递静态属性的任何地方,您也可以传递枚举实例。
将枚举的标志设置为给定的标志数组。
$permissions = UserPermissions::flags([UserPermissions::ReadComments]);$permissions->flags([UserPermissions::EditComments, UserPermissions::DeleteComments]); // 标志现在为:EditComments、DeleteComments。
将给定标志添加到枚举中
$permissions = UserPermissions::flags([UserPermissions::ReadComments]);$permissions->addFlag(UserPermissions::EditComments); // 标志现在为:ReadComments、EditComments。
将给定的标志添加到枚举中
$permissions = UserPermissions::flags([UserPermissions::ReadComments]);$permissions->addFlags([UserPermissions::EditComments, UserPermissions::WriteComments]); // 标志现在为:ReadComments、EditComments、WriteComments。
将所有标志添加到枚举中
$permissions = UserPermissions::flags([UserPermissions::ReadComments]);$permissions->addAllFlags(); // 枚举现在拥有所有标志
从枚举中删除给定的标志
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->removeFlag(UserPermissions::ReadComments); // 标志现在是:WriteComments。
从枚举中删除给定的标志
$permissions = UserPermissions::flags([UserPermissions::ReadComments、UserPermissions::WriteComments、UserPermissions::EditComments]);$permissions->removeFlags([UserPermissions::ReadComments、UserPermissions::WriteComments]); // 标志现在是:EditComments。
从枚举中删除所有标志
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->removeAllFlags();
检查枚举是否具有指定的标志。
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->hasFlag(UserPermissions::ReadComments); // True$permissions->hasFlag(UserPermissions::EditComments); // 错误的
检查枚举是否具有所有指定的标志。
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->hasFlags([UserPermissions::ReadComments, UserPermissions::WriteComments]); // True$permissions->hasFlags([UserPermissions::ReadComments, UserPermissions::EditComments]); // 错误的
检查枚举是否没有指定的标志。
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->notHasFlag(UserPermissions::EditComments); // True$permissions->notHasFlag(UserPermissions::ReadComments); // 错误的
检查枚举是否没有任何指定的标志。
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->notHasFlags([UserPermissions::ReadComments, UserPermissions::EditComments]); // True$permissions->notHasFlags([UserPermissions::ReadComments, UserPermissions::WriteComments]); // 错误的
将标志作为实例数组返回。
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->getFlags(); // [UserPermissions::ReadComments(), UserPermissions::WriteComments()];
检查枚举上是否设置了多个标志。
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->hasMultipleFlags(); // True;$permissions->removeFlag(UserPermissions::ReadComments)->hasMultipleFlags(); // 错误的
获取枚举的位掩码。
UserPermissions::Member()->getBitmask(); // 11;UserPermissions::Moderator()->getBitmask(); // 111;UserPermissions::Admin()->getBitmask(); // 1111;UserPermissions::DeleteComments()->getBitmask(); // 1000;
要直接在 Eloquent 查询中使用标记枚举,您可以在模型上使用QueriesFlaggedEnums
特征,它为您提供以下方法:
User::hasFlag('权限', UserPermissions::DeleteComments())->get();
User::notHasFlag('权限', UserPermissions::DeleteComments())->get();
User::hasAllFlags('权限', [UserPermissions::EditComment(), UserPermissions::ReadComment()])->get();
User::hasAnyFlags('权限', [UserPermissions::DeleteComments(), UserPermissions::EditComments()])->get();
您可以使用 Laravel 内置的自定义转换将模型属性转换为枚举。这会在获取时将属性转换为枚举实例,并在设置时返回到枚举值。由于Enum::class
实现了Castable
合约,因此您只需指定枚举的类名:
使用 BenSampoEnumTestsEnumsUserType;使用 IlluminateDatabaseEloquentModel;类示例扩展模型 {protected $casts = ['random_flag' => 'boolean', // 标准 Laravel 类型转换示例 'user_type' => UserType::class, // 枚举类型转换示例]; }
现在,当您访问Example
模型的user_type
属性时,基础值将作为UserType
枚举返回。
$example = Example::first();$example->user_type // UserType 的实例
查看枚举实例上可用的方法和属性,以充分利用属性转换。
您可以通过传递枚举值或另一个枚举实例来设置该值。
$example = Example::first();// 使用枚举值设置$example->user_type = UserType::Moderator;// 使用枚举实例设置$example->user_type = UserType::Moderator();
$model->toArray()
行为当使用toArray
(或从控制器返回模型作为响应)时,Laravel 将调用枚举实例上的toArray
方法。
默认情况下,这将仅返回其本机类型的值。您可能还想访问其他属性(键、描述),例如返回 javascript 应用程序。
要自定义此行为,您可以重写枚举实例上的toArray
方法。
// 示例 Enumfinal 类 UserType 扩展 Enum {常量管理员 = 0;常量主持人 = 1; }$instance = UserType::Moderator();// 默认公共函数 toArray() {返回$this->值; }// 返回 int(1)// 返回所有属性 public function toArray() {返回$这个; }// 返回所有属性的数组 // array(3) {// ["value"]=>// int(1)"// ["key"]=>// string(9) "MODERATOR "// ["描述"]=>// string(9) "主持人"// }
许多数据库将所有内容都作为字符串返回(例如,整数可能作为字符串'1'
返回)。为了减少库用户的摩擦,我们使用类型强制来找出预期值。如果您想控制它,您可以重写枚举类上的parseDatabase
静态方法:
最终类 UserType 扩展了 Enum {const 管理员 = 0;const 主持人 = 1;公共静态函数 parseDatabase($value) {返回(int)$值; } }
从parseDatabase
方法返回null
将导致模型上的属性也为null
。如果您的数据库存储不一致的空白值(例如空字符串而不是NULL
这会很有用。
如果您将模型上的属性转换为枚举,则 laravel-ide-helper 包可用于自动为您生成属性文档块。
由于枚举在代码级别强制执行一致性,因此无需在数据库级别再次执行此操作,因此数据库列的推荐类型是string
或int
具体取决于您的枚举值。这意味着您可以在代码中添加/删除枚举值,而无需担心数据库层。
使用 AppEnumsUserType;使用 IlluminateSupportFacadesSchema;使用 IlluminateDatabaseSchemaBlueprint;使用 IlluminateDatabaseMigrationsMigration;类 CreateUsersTable 扩展迁移 {/** * 运行迁移。 * * @return void */public function up(): void{ Schema::table('users', function (Blueprint $table): void {$table->bigIncrements('id');$table->timestamps();$table->string('type') ->默认(UserType::主持人); }); } }
enum
类型或者,您可以在迁移中使用Enum
类来定义枚举列。枚举值必须定义为字符串。
使用 AppEnumsUserType;使用 IlluminateSupportFacadesSchema;使用 IlluminateDatabaseSchemaBlueprint;使用 IlluminateDatabaseMigrationsMigration;类 CreateUsersTable 扩展迁移 {/** * 运行迁移。 * * @return void */public function up(): void{ Schema::table('users', function (Blueprint $table): void {$table->bigIncrements('id');$table->timestamps();$table->enum('type', UserType::获取值()) ->默认(UserType::主持人); }); } }
您可以使用EnumValue
规则验证传递给控制器的枚举值是否是给定枚举的有效值。
使用 BenSampoEnumRulesEnumValue;公共函数存储(请求 $request) {$this->validate($request, ['user_type' => ['required', new EnumValue(UserType::class)], ]); }
默认情况下,类型检查设置为严格,但您可以通过将false
传递给 EnumValue 类的可选第二个参数来绕过此设置。
new EnumValue(UserType::class, false) // 关闭严格类型检查。
您还可以使用EnumKey
规则验证键。例如,如果您将枚举键作为 URL 参数进行排序或过滤,则这非常有用。
使用 BenSampoEnumRulesEnumKey;公共函数存储(Request $request) {$this->validate($request, ['user_type' => ['required', new <span class="pl-v"