不再建議使用該程式庫,尤其是對於新專案。 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 無法實例化,則傳回 null。
如果您希望 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::寫評論; // 讀取與寫入。 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]);$permissions->removeFlags([UserPermissions::ReadComments])::Permissions::WriteComments); // 標誌現在是:EditComments。
從枚舉中刪除所有標誌
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->removeAllFlags();
檢查枚舉是否有指定的標誌。
$permissions = UserPermissions::flags([UserPermissions::ReadComments, UserPermissions::WriteComments]);$permissions->hasFlag(UserPermissions::ReadComments); // 真$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); // 真$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(); // 真;$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"