How to use the new PHP enum in Laravel
PHP 8.1 introduced long awaited support for built-in Enums. There is no more need for custom solutions in your Laravel projects since Laravel with its 8.69 release has your back.
We’ll take a really simple example of a blogpost model with a publication state that can be draft or published. As it will be stored in database, we used a “backed” Enum. It means that each value is associated with a string.
namespace App\Enums; enum BlogpostState: string{ case DRAFT = 'state_draft'; case PUBLISHED = 'state_published';}
Entity model
When creating your database schema, you can use eloquent’s“string” type for the state column as it will store the Enum’s backed value as a string: ‘state_draft’ or ‘state_published’.
public function up(){ Schema::create('blogposts', function (Blueprint $table) { ... $table->string('state'); });}
Now comes the great part. You can use the new casting in your Blogpost model and it allows your state model attribute to be casted to/from an Enum:
namespace App\Models; class Blogpost extends Model{ protected $casts = [ 'state' => BlogpostState::class, ]; }
Then you’ll get the enum when interacting with the state attribute:
>>> dd($blogpost->state);=> App\Enums\BlogpostState { +name: "DRAFT", +value: "state_draft", }
So for example you can now compare the state to the Enum directly:
public function isPublished(){ return $this->state === BlogpostState::PUBLISHED;}
Or access the backed value stored in database if needed:
>>> dd($blogpost->state->value);=> "state_draft"
Also if you try to create or update a blogpost with an invalid state value, you’ll get an immediate PHP error:
>>> $blogpost->update(['state' => 'state_foo']);=> PHP Error: "state_foo" is not a valid backing value for enum"App\Models\Enums\BlogpostState"
The state’s enum can have methods,
public function label(): string{ return match($this) { self::_DRAFT_ => 'Draft...', self::_PUBLISHED_ => 'Published!', };}
that could be used directly with your model:
>>> $blogpost->state->label();=> "Draft..."
Validation rule
When you want to make sure an input match an enum, you can use a new laravel rule:
namespace App\Http\Requests; class BlogpostRequest extends FormRequest{ public function rules() { return [ "state" => [ "required", new Enum(BlogpostState::class) ], ]; }}
And it’s pretty straightforward. Now your request only accept enum’s backed values: ‘state_draft’ or ‘state_published’.
Factory
Also, when you want to seed a blogpost, you can now use enum directly in your factory:
namespace Database\Factories; class BlogpostFactory extends Factory{ public function definition() { return [ ... 'state' => BlogpostState::DRAFT, ]; }}
Route bindings
Since Laravel 9, there is now implicit route binding with enum. So you can type-hint your enum and Laravel will automatically cast the string value from the URL :
Route::get('/blog/{state}', function(BlogpostState $state) { // here $state is an enum instance});
In our example, both
- https://mysite.com/blog/state_draft
- https://mysite.com/blog/state_published
are valid URLs. But if URL does not match any of the enum’s values, you’ll get a 404 error (Ex: https://mysite.com/blog/state_foo).
I think we’re done here. Thanks for reading !