Использование этой библиотеки больше не рекомендуется, особенно для новых проектов. PHP 8.1 изначально поддерживает перечисления.
См. № 332.
Простая, расширяемая и мощная реализация перечисления для Laravel.
Перечислить пары ключ-значение как константы класса
Полнофункциональный набор методов
Создание экземпляра перечисления
Помеченные/побитовые перечисления
Подсказка типа
Приведение атрибутов
Enum ремесленный генератор
Правила проверки для передачи ключа или значений перечисления в качестве входных параметров
Поддержка локализации
Расширяемый с помощью макросов
Создано Беном Сэмпсоном
Гид
Установка
Переход на собственные перечисления PHP
Библиотека перечислений
Основное использование
Определение перечисления
Создание экземпляра
Свойства экземпляра
Кастинг экземпляров
Экземплярное равенство
Тип подсказки
Помеченное/побитовое перечисление
Кастинг атрибутов
Миграции
Валидация
Локализация
Настройка описаний
Настройка описания класса
Настройка описаний значений
Расширение базового класса Enum
Интеграция Laravel Nova
Интеграция PHPStan
Список ремесленных команд
Справочник по классам Enum
Незавершённые версии
Вы читаете документацию для 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 или выше
Ларавел 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()
: реализуйте альтернативу.
блоки try/catch, обрабатывающие BenSampoEnumExceptionsInvalidEnumKeyException
или BenSampoEnumExceptionsInvalidEnumMemberException
. Либо перехватите ValueError
выдаваемый собственными перечислениями, либо переключитесь на использование tryFrom()
и обработайте null
.
После преобразования всех перечислений вы можете удалить зависимость от этой библиотеки.
Просмотрите и загрузите список часто используемых перечислений, предоставленных сообществом.
Библиотека перечислений →
Вы можете использовать следующую команду Artisan для создания нового класса перечисления:
PHP Artisan make:enum UserType
Теперь вам просто нужно добавить возможные значения, которые ваше перечисление может иметь в качестве констант.
<?php Declare(strict_types=1);пространство имен AppEnums;используйте BenSampoEnumEnum;финальный класс UserType расширяет 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, если экземпляр Enum не может быть создан.$enumInstance = UserType::coerce($someValue);
Если вы хотите, чтобы ваша IDE автоматически заполняла статические помощники создания экземпляров, вы можете генерировать аннотации PHPDoc с помощью команды artisan.
По умолчанию все перечисления в app/Enums
будут аннотированы (вы можете изменить папку, передав путь к --folder
).
Перечисление PHP Artisan: аннотация
Вы можете аннотировать один класс, указав имя класса.
перечисление php artisan: аннотация "AppEnumsUserType"
Если у вас есть экземпляр перечисления, вы можете получить доступ к key
, value
и description
как к свойствам.
$userType = UserType::fromValue(UserType::SuperAdministrator);$userType->key; // СуперАдминистратор$userType->value; // 3$userType->описание; // Суперадминистратор
Это особенно полезно, если вы передаете экземпляр перечисления в представление блейда.
Экземпляры Enum могут быть преобразованы в строки, поскольку они реализуют магический метод __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(['random-value']); // истинный
Экземплярные перечисления не являются одиночными, а каждый раз создается новый объект. Таким образом, строгое сравнение ===
разных экземпляров перечисления всегда будет возвращать 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)) {return true; } вернуть ложь; }$userType1 = UserType::fromValue(UserType::SuperAdministrator);$userType2 = UserType::fromValue(UserType::Moderator);canPerformAction($userType1); // Возвращает truecanPerformAction($userType2); // Возвращает ложь
Стандартные перечисления представляют одно значение одновременно, но помеченные или побитовые перечисления могут представлять несколько значений одновременно. Это делает их идеальными, когда вы хотите выразить несколько вариантов выбора из ограниченного набора опций. Хорошим примером этого могут быть пользовательские разрешения, когда существует ограниченное количество возможных разрешений, но пользователь может не иметь ни одного, некоторых или всех из них.
Вы можете создать помеченное перечисление, используя следующую ремесленную команду:
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;// Shortcutsconst Member = self::ReadComments | self::WriteComments; // Чтение и запись.const Moderator = self::Member | себя::РедактироватьКомментарии; // Все разрешения, которые имеет участник, плюс Edit.const Admin = self::Moderator | self::DeleteComments; // Все разрешения, которые имеет модератор, плюс удаление.}
Есть несколько способов создать экземпляр помеченного перечисления:
// Стандартный новый класс 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(); // Enum теперь имеет все флаги
Удалить данный флаг из перечисления
$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('permissions', UserPermissions::DeleteComments())->get();
User::notHasFlag('permissions', UserPermissions::DeleteComments())->get();
User::hasAllFlags('permissions', [UserPermissions::EditComment(), UserPermissions::ReadComment()])->get();
User::hasAnyFlags('permissions', [UserPermissions::DeleteComments(), UserPermissions::EditComments()])->get();
Вы можете преобразовать атрибуты модели в перечисления, используя встроенное в Laravel пользовательское преобразование. Это приведет к приведению атрибута к экземпляру перечисления при получении и обратно к значению перечисления при установке. Поскольку Enum::class
реализует контракт Castable
, вам просто нужно указать имя класса перечисления:
используйте BenSampoEnumTestsEnumsUserType; используйте IlluminateDatabaseEloquentModel; Пример класса расширяет модель {protected $casts = ['random_flag' => 'boolean', // Пример стандартного приведения laravel'user_type' => UserType::class, // Пример приведения перечисления]; }
Теперь, когда вы получаете доступ к атрибуту user_type
вашей модели Example
, базовое значение будет возвращено в виде перечисления UserType
.
$example = Пример::first();$example->user_type // Экземпляр UserType
Просмотрите методы и свойства, доступные в экземплярах перечисления, чтобы максимально эффективно использовать приведение атрибутов.
Вы можете установить значение, передав значение перечисления или другой экземпляр перечисления.
$example = Пример::first();// Устанавливаем с помощью перечисления value$example->user_type = UserType::Moderator;// Устанавливаем с помощью перечисления instance$example->user_type = UserType::Moderator();
$model->toArray()
При использовании toArray
(или возврате модели/моделей из вашего контроллера в качестве ответа) Laravel вызовет метод toArray
в экземпляре перечисления.
По умолчанию это вернет только значение в его собственном типе. Возможно, вам также понадобится доступ к другим свойствам (ключу, описанию), например, чтобы вернуться в приложение JavaScript.
Чтобы настроить это поведение, вы можете переопределить метод toArray
в экземпляре перечисления.
// Пример класса Enumfinal UserType расширяет Enum {const АДМИНИСТРАТОР = 0; const МОДЕРАТОР = 1; }$instance = UserType::Moderator();// Публичная функция по умолчанию toArray() {вернуть $this->значение; }// Возвращает int(1)// Возвращает все свойства public функции toArray() {вернуть $это; }// Возвращает массив всех свойств // array(3) {// ["value"]=>// int(1)"// ["key"]=>// string(9) "MODERATOR "// ["description"]=>// string(9) "Модератор"// }
Многие базы данных возвращают все в виде строк (например, целое число может быть возвращено в виде строки '1'
). Чтобы уменьшить неудобства для пользователей библиотеки, мы используем приведение типов для определения предполагаемого значения. Если вы предпочитаете контролировать это, вы можете переопределить статический метод parseDatabase
в своем классе перечисления:
последний класс UserType расширяет Enum {const администратор = 0; const модератор = 1; общедоступная статическая функция parseDatabase ($ значение) {return (int) $value; } }
Возврат значения null
из метода parseDatabase
приведет к тому, что атрибут модели также будет иметь 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') ->default(UserType::Moderator); }); } }
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:: получить значения()) ->default(UserType::Moderator); }); } }
Вы можете проверить, что значение перечисления, переданное контроллеру, является допустимым значением для данного перечисления, используя правило EnumValue
.
используйте BenSampoEnumRulesEnumValue; общедоступное хранилище функций (Запрос $request) {$this->validate($request, ['user_type' => ['required', new EnumValue(UserType::class)], ]); }
По умолчанию для проверки типов установлено строгое значение, но вы можете обойти это, передав false
необязательному второму параметру класса EnumValue.
new EnumValue(UserType::class, false) // Отключаем строгую проверку типов.
Вы также можете проверить ключи, используя правило EnumKey
. Это полезно, если вы используете ключ перечисления в качестве параметра URL, например, для сортировки или фильтрации.
используйте BenSampoEnumRulesEnumKey; общедоступное хранилище функций (Запрос $request) {$this->validate($request, ['user_type' => ['required', new <span class="pl-v"