С выпуском PHP 8.1 язык получил встроенную поддержку перечислений. Перечисления — это типобезопасный, читаемый и эффективный способ инкапсуляции небольшого набора возможных значений, которые поле может принимать в модели данных. Использование классов вместо перечислений базы данных обеспечивает большую гибкость, если в будущем потребуется добавить в список.
Например, у вас есть модель пользовательских данных, и у этого пользователя может быть определенный список ролей, которые вы можете выбрать.
namespace App\Enums;
enum UserRole: string
{
case Admin = 'admin';
case TeamAdmin = 'team_admin';
case Support = 'support';
case Basic = 'basic';
}
В модели данных Laravel также поддерживает перечисления, приводя их к значению, если оно определено в массиве casts.
/**
* The attributes that should be cast.
*
* @var array<string,string|class-string>
*/
protected $casts = [
'role' => UserRole::class,
];
Добавление перечисления обеспечит создание исключения, если мы попытаемся сохранить роль в нашей пользовательской модели, не определенной в перечислении.
Очень практичное использование перечислений заключается в генерации значений для раскрывающегося списка в вашем HTML
<select name=”roles”>
@foreach(UserRole::cases() as $role)
<option value="{{ $role->value }}">{{ $role->name }}</option>
@endforeach
</select>
На первый взгляд, в приведенном выше примере нет ничего плохого, пока вы не посмотрите на видимое имя каждого параметра в раскрывающемся списке. Admin и TeamAdmin — отличные имена переменных, но администратора и администратора команды было бы лучше представить в пользовательском интерфейсе, чтобы было предельно ясно, какова роль человека, управляющего ролями пользователей.
Хотя перечисления отлично подходят для простых пар имя/значение, в таких случаях, когда вам нужно добавить 3-е свойство, вы должны проявить творческий подход.
Введите атрибуты PHP. Заимствованный из концепции аннотаций в других языках, это способ связать метаданные со свойствами, методами и классами, что звучит точно так же, как нам нужно.
Во-первых, нам нужно создать атрибут Description.
namespace App\Enums\Attributes;
use Attribute;
#[Attribute]
class Description
{
public function __construct(
public string $description,
) {
}
}
Теперь у нас есть атрибут Description, который мы можем использовать в нашем перечислении, чтобы мы могли определить удобные для пользователя имена ролей, которые мы хотим.
namespace App\Enums;
enum UserRole: string
{
#[Description('Administrator')]
case Admin = 'admin';
#[Description('Team Administrator')]
case TeamAdmin = 'team_admin';
case Support = 'support';
case basic = 'basic';
}
Теперь нам нужно извлечь эти атрибуты, что может быть сделано только через отражение. Поскольку мы можем захотеть повторно использовать этот атрибут в других перечислениях, мы захотим сделать черту, чтобы сделать это проще.
namespace App\Enums\Concerns;
use Illuminate\Support\Str;
use ReflectionClassConstant;
use App\Enums\Attributes\Description;
trait GetsAttributes
{
/**
* @param self $enum
*/
private static function getDescription(self $enum): string
{
$ref = new ReflectionClassConstant(self::class, $enum->name);
$classAttributes = $ref->getAttributes(Description::class);
if (count($classAttributes) === 0) {
return Str::headline($enum->value);
}
return $classAttributes[0]->newInstance()->description;
}
}
Если разбить этот метод, то первые 2 строки используют отражение для получения атрибутов перечисления. Поскольку не каждое перечисление может иметь атрибут Description, мы устанавливаем резервный вариант для преобразования значения (или имени) этого перечисления в качестве нашего описания.
Наконец, мы извлекаем значение описания из атрибутов перечисления. Мы можем добавить еще один метод к нашей черте, чтобы справиться с этим.
/**
* @return array<string,string>
*/
public static function asSelectArray(): array
{
/** @var array<string,string> $values */
$values = collect(self::cases())
->map(function ($enum) {
return [
'name' => self::getDescription($enum),
'value' => $enum->value,
];
})->toArray();
return $values;
}
Теперь, в нашем HTML, мы можем просто изменить метод, который мы вызываем для класса
<select name=”roles”>
@foreach(UserRoles::asSelectArray() as $role)
<option value=”{{ $role->value }}”>{{ $role->name }}</option>
@endforeach
</select>
перечисления Хотя они являются относительными новичками в PHP, перечисления и атрибуты являются отличными дополнениями к языку и обеспечивают встроенную поддержку для многих распространенных случаев использования.