• Время чтения ~1 мин
  • 28.03.2023

С выпуском 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, перечисления и атрибуты являются отличными дополнениями к языку и обеспечивают встроенную поддержку для многих распространенных случаев использования.

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

Про мене

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

Об авторе CrazyBoy49z
WORK EXPERIENCE
Контакты
Ukraine, Lutsk
+380979856297