Using enums in Laravel
I'm a big fan of enums. Having recently worked for a company who use C#, where enums are used extensively, I've got used to being able to reach for them and miss them when they're not available.
I use them for anything with a limited set of possible options, such as days of the week, the status of an order or, as in the examples below, user type.
There are several benefits to using enums:
- Reduces errors caused by transposing or mistyping numbers.
- Makes it easy to change values in the future.
- Makes code easier to read, which means it is less likely that errors will creep into it.
- Ensures forward compatibility. With enumerations, your code is less likely to fail if in the future someone changes the values corresponding to the member names.
Enums aren't natively supported in PHP but an equivalent is fairly easy to achieve using constants on a class. Futhermore I've created a Laravel package called laravel-enum which allows you access helper functions such as listing keys and values, attaching descriptions to values, and validating requests which are expecting enum values.
This guide walks through the process of installing the Laravel package and includes examples of usage and best practice.
Installing the package
The package can be installed via composer by running the following in your terminal:
$ composer require bensampo/laravel-enum
If you're using a version of Laravel below 5.5, you'll need to add the service provider to config/app.php
.
'BenSampo\Enum\EnumServiceProvider'
Creating your first enum
We're going to create an enum for user types. In our example application, a user can belong to any one of three user types: administrator, paid member, member.
The package includes a generator for creating enums, so you can run the following command to create an enum called UserType. The file will be created at app/Enums/UserType.php
.
php artisan make:enum UserType
You'll see a certain amount of scaffolding in this file already. Near the top of the file, the list of possible options are defined as constants. The constant values are what's stored in the database so generally I find it best to use integers, but there's no restriction, as long as each value is unique.
The options for our example will look like this:
const Administrator = 0;
const PaidMember = 1;
const Member = 2;
Storing values in a database
Now that we have an enum with some possible values we can start using it. In our migration for our user table, we can add the following.
$table->tinyInteger('type')->unsigned()->default(UserType::Member);
Since null isn't an option for enums, we need to give it a default. In this case, it makes sense to assume that users are going to be standard members by default.
Make sure to include the use statement for this enum at the top of the file.
use App\Enums\UserType;
Using the enum in operations
Since our user model now has a type property on it we can access it and compare it against our enum. This is where the true power of enums lie and why I like them so much. Take a look at the following usage example and the possible alternatives.
if ($user->type === UserType::PaidMember) {
// Do some paid member only stuff here.
}
If we weren't using enums, we might have something like these:
if ($user->type === 1) { // What does '1' mean??
// ...
}
if ($user->type === 'PaidMember') { // Magic string 😞
// ...
}
The first example using enums is both the most readable and the least prone to error. If I had happened to type UserType::Paidember
(notice the typo) by accident, I would get a nice error notifying me of the fact rather than the code failing silently as would happen in the first two examples.
Displaying the enum
On some pages in our app, we might like to show the user which user type they belong to. If we simply output the value from the database we're going to get an integer, which is clearly not what we want. Instead we can use the getDescription
method which exists on the base class for every enum.
In our blade template we can do the following:
<p>{{ \App\Enums\UserType::getDescription($user->type) }}</p>
Which will output either Administrator
, PaidMember
or Member
.
Sometimes the key names are compound words (like PaidMember
in our example) or we want to display something different to the key name. We can override the description for each key in the enum like the following. In app/Enums/UserType.php
:
public static function getDescription(int $value): string
{
switch ($value) {
case self::PaidMember:
return 'Paid member';
break;
default:
return self::getKey($value);
}
}
Now when calling getDescription
we'll get either Administrator
, Paid member
or Member
.
Validation
When handling modifications to an enum through user input, it's a very good idea to make sure we only accept valid enum values. To do this we can use the validation rule included in the package.
I often display enums as a HTML select element when displaying them in a form. This is still open to malicious alterations, but helps avoid cases where an incorrect value is passed to the server by mistake.
Continuing with our user type example, let's handle changing the value in our user controller:
public function store(Request $request)
{
$this->validate($request, [
'type' => ['required', new EnumValue(UserType::class)],
]);
// Save user etc...
}
Remember to include both the use statements for the enum and the EnumValue
rule.
use App\Enums\UserType;
use BenSampo\Enum\Rules\EnumValue;
Wrapping up
Of course, this isn't the full extent of what can be done with enums, but it covers the 90% use case.
It's worth taking a look at the full list of methods on the enum base class to see what else you might be able to achieve.