Why you should be using backed enums in Laravel

Another year, another new “killer” feature for the poor, embattled software developer to learn rears its ugly head.

Fortunately, enums only take a few minutes to learn and can be pretty useful for standardising database columns.

Are they going to change the world? Probably not. Are they going to make a few problems slightly less problem-y? Quite possibly.

Note: Enums require PHP 8.1+ and Laravel 9

What's a enum?

The Merriam-Webster dictionary defines an “enum” as an “enumerated type”. This type is then limited to a selection of choices or values. A common example is drawn from the world of playing cards:

1enum Suit
2{
3 case Hearts;
4 case Diamonds;
5 case Clubs;
6 case Spades;
7}

What's a backed enum?

A backed enum is an enumerated type where the values are backed up by a primitive value such as a string or integer.

1enum Suit: string
2{
3 case Hearts = "hearts";
4 case Diamonds = "diamonds";
5 case Clubs = "clubs";
6 case Spades = "spades";
7}

Why use backed enums over regular ones?

Standard enums are fine as long as all of your code operates inside the PHP processor (yes, we know we just said “processor” twice). However, when you start needing to pass those enums to your frontend or saving them in a database, it gets a lot trickier to deal with them. Backed enums allow you to transition seamlessly between typed variables and primitive values.

But what's the point of them?

Let’s take a solid example. You have an Admin model and each admin can have a role assigned. You’re not planning to have more than a couple of roles, so you decide to just save the role as a string in the admins table. Your available roles are: ‘editor’, ‘writer’ and ‘proofreader’.

Throughout your entire codebase, you now have to rely on every developer using (1) a valid role, (2) spelling it correctly. Even as a single developer, it’s easy to forget the decisions of yesterday. “Was it ‘proof-reader’ or ‘proofreader’?”, “Do I have a role for ‘guest-writer’ or am I just imagining that?”.

Backed enums give you a central source of truth for the values that can used, and, thanks to Laravel, it’s extremely easy to incorporate them into your models.

Enums and Laravel

So we’ve defined our roles enum a little like this:

1enum Role: string
2{
3 case EDITOR = "editor";
4 case WRITER = "writer";
5 case PROOFREADER = "proof-reader";
6}

and want to send the values to the frontend to allow an admin panel to assign a role to a new user. We can simply call Role::cases() and get an array of primitive values for our frontend, namely ["editor","writer","proof-reader"].

In our frontend of choice, we’ve put those options inside a select input and now we want to process the value in our form data. To do that, we’d use Laravel’s built-in enumeration validator.

1use App\Enums\Role;
2use Illuminate\Validation\Rules\Enum;
3
4$validated = $request->validate([
5 'role' => [new Enum(Role::class)],
6]);

When it comes to saving that value to the database, Laravel once again has us covered with an enum cast that we can add to our Admin model:

1use App\Enums\Role;
2
3class Admin extends Authenticatable
4{
5 use Notifiable;
6
7 protected $casts = [
8 'role' => Role::class,
9 ];
10}

What this means is that now, you can send your enum values to the frontend as an array of strings. A user can then send you up a value which is automatically validated as belonging to the options you defined in your enum class. Finally, whenever you pass that enum to or from the database layer, it is automatically cast to a string type that the database can handle.

How to use enums inside your application

Inside your app, you can now be confident that the role property of any admin instance is now a backed enum. So checking is as simple as:

if ($admin->role !== Role::EDITOR) {
abort(403);
}

Try to assign ‘edtior’ or ‘witer’ to an admin and you’ll quickly find yourself with an easy-to-resolve error. Having your options defined as constants also carries the benefit of IDE autocompletion. No need to spend precious time verifying that you’ve used the correct role name in the correct case or trying to work out if there was a slightly different option that would’ve been more appropriate. All of your choices are in one place and are safely typed.

Enum columns in migrations

“But,” you say. “What about the enum column type in Laravel migrations? Why don’t we use those?”

$table->enum('role', ['editor', 'writer','proof-reader']);

You’re obviously free to use them. The problem is that firstly, you have to keep your enum class and your enum database column in sync by maintaining two sources of truth. Secondly, it requires a database migration every time you want to add or remove a role from your app with no real benefit. Thirdly, have you tried finding information from your migrations folder? It’s a nightmare! Don’t forget to check for any table updates to ensure which options have been added or removed!

Conclusion

Backend enums can make a lot of sense when you have a database column or other value that requires adhering to a strictly defined number of options. They offer the benefit of consistency in value as well as IDE compatibility for easy autocompletion. VSCode doesn’t know that you mean when you start typing if ($admin->role === 'edi..., but it does know what if ($admin->role === Role::EDI… means.

They’re probably not going to revolutionise your Laravel experience, but they will make ensuring value consistency between your frontend, application and database layer a little easier.

Copyright 2007 - 2024 southcoastweb is a brand of DSM Design.