Working with Enums

Tips and handy utilities for working with enums in your Laravel project

Making your Enum invokable

When working with enums, its handy to reference them statically such as when authorizing access to a controller method using a role name.

Personally, I find it clumsy to have to access the Enum static method such as

$this->authorize(Role::MANAGER->name);

With a tiny function we can simplify this to

$this->authorize(Role::MANAGER());

This is because we can make the enum invokable using the magic __callStatic() method on the enum. This method is called when a static method is requested but does not exist. Using this we can return an instance of the Enum being used.

An example of an enum class for expenses, using this method;

App\Enums\Expenses.php
<?php

namespace App\Enums;

use Illuminate\Support\Arr;

enum Expenses
{
  case CREATE_EXPENSE;
  case DELETE_EXPENSE;
  case APPROVE_EXPENSE;

  public static function __callStatic($name, $args)
  {
    $case = Arr::first(static::cases(), fn($case) => $case->name === $name);

    throw_unless($case, sprintf('Undefined Enum Case %s::%s',static::class,$name));
    
    return empty($case->value) ? $case->name : $case->value;
  }
}

Now

Expenses::CREATE_EXPENSE()   // "CREATE_EXPENSE"

Why not just type CREATE_EXPENSE directly as a string? Well, the power of enums (and hopefully why you are using them) is because you cannot accidentally use a string which is not declared in your application Expenses::CRAETE_EXPENSE() will throw and exception whereas "CRAETE_EXPENSE" will not.

Adding this method to your enums does not remove any existing enum abilities

Invokable Enum with Backed Enums

The method shown supports both plain Enums and Backed Enums

A Backed Enum returns a value (string or int) rather than the enum name

For instance, if we wanted to just work with ints in the database, our Enum might be declared as

enum Expenses: int
{
  case CREATE_EXPENSE = 1;
  case DELETE_EXPENSE = 2;
  case APPROVE_EXPENSE = 3;

Now;

Expenses::CREATE_EXPENSE()   // 1

The method returns the name or the value, depending on whether your enum is backed.

Make a Trait

Since the function is not dedicated to any specific enum, you can create a trait and include it in every enum class.

App\Enums\Traits
<?php

namespace App\Enums\Traits;

use Illuminate\Support\Arr;

trait Invokable
{
    public static function __callStatic($name, $args)
    {
      $case = Arr::first(static::cases(), fn($case) => $case->name === $name);
  
      throw_unless($case, sprintf('Undefined Enum Case %s::%s',static::class,$name));
  
      return empty($case->value) ? $case->name : $case->value;

    }
}

And then use in every enum

<?php

namespace App\Enums;

use App\Enums\Traits\Invokable;

enum Expenses:string
{
    use Invokable;
    
    case CREATE_EXPENSE = 'can create expense';
    case DELETE_EXPENSE = 'can delete expense';
    case APPROVE_EXPENSE = 'can approve expense';

}

Enum methods

Enums are not like normal classes, but they do still allow public methods which may be called against a given Enum case.

For Instance;

Returning an icon component for a case

    public function icon()
    {
        return match($this) {
            self::CREATE_EXPENSE => '<x-icons-create-expense />',
            self::DELETE_EXPENSE => '<x-icons-delete-expense />',
            self::APPROVE_EXPENSE => '<x-icons-approve-expense />',
        };
    }

The match statement is very useful for this type of thing and allows an easy way to return different data for each case.

Returning a long block of text

    public function longDescription()
    {
        return match($this) {
            self::CREATE_EXPENSE => $this->createDescription(),
            self::DELETE_EXPENSE => $this->deleteDescription(),
            self::APPROVE_EXPENSE => $this->approveDescription(),
        };
    }

    private function createDescription()
    {
        return "A user with this permission is allowed to create expense records for approval by the accounting team";
    }

And then use it in blade like

{{ Expenses::APPROVE_EXPENSE->longDescription() }}

Or if you are already working with an instance of an enum

{{ $permission->longDescription() }}

If you are having problems with using enum classes directly in your blade files, dont forget that you can import the class at the top of your blade file with @use('\App\Enums\Expenses')

Last updated