Could we help you? Please click the banners. We are young and desperately need the money
If you're building Laravel applications, you've probably used strings or integers to represent fixed sets of values—things like user roles, order statuses, or payment methods. While this approach works, it's prone to bugs, typos, and inconsistencies that can haunt your application. Enums are Laravel's solution to this problem, and understanding how to use them effectively is one of the best investments you can make as a junior developer.
In this guide, we'll explore the practical benefits of using enums in Laravel applications, show you real-world examples, and explain why modern Laravel development increasingly relies on this powerful feature.
An enum (enumeration) is a special data type that allows you to define a fixed set of possible values. Instead of using strings or integers scattered throughout your code, enums provide a centralized, type-safe way to represent these values.
Laravel's enum support leverages PHP 8.1+ native enums, introduced in version 8.75. If you're running Laravel 8.75 or higher with PHP 8.1+, you're ready to use enums in your applications.
Many junior developers start by hardcoding values directly into their code:
// Without enums - error-prone approach
if ($user->role === 'admin') {
// Admin logic
}
if ($order->status === 'pending') {
// Process pending order
}
This approach creates several problems: typos go undetected until runtime, values are scattered across your codebase, and refactoring becomes a nightmare. If you misspell 'admin' as 'admi' somewhere, PHP won't complain—but your logic will silently fail.
With enums, you define all valid values in one place:
// Define your enum
enum UserRole: string {
case Admin = 'admin';
case Editor = 'editor';
case Subscriber = 'subscriber';
}
// Use it with type safety
if ($user->role === UserRole::Admin) {
// Admin logic
}
// Or in your model
class User extends Model {
protected $casts = [
'role' => UserRole::class,
];
}
Now, if you try to assign an invalid value, PHP will throw an error immediately. You catch bugs at development time, not in production.
One of the biggest advantages of enums is type safety. Your IDE and static analysis tools can validate that you're using valid enum values, preventing entire categories of bugs:
// With enums, this is caught immediately
$order->status = 'compleeted'; // Error! Case doesn't exist
$order->status = OrderStatus::Completed; // Correct!
// Type hints in methods
public function updateStatus(OrderStatus $status): void {
$this->status = $status;
}
// Now invalid values can't be passed
updateStatus('invalid'); // Type error
updateStatus(OrderStatus::Shipped); // Type-safe
This type safety is especially valuable in larger teams where junior developers might not be familiar with all the valid values your application uses. The code itself communicates the valid options.
Enums make your code self-documenting. Instead of wondering "What values can status hold?", developers can simply look at the enum definition:
enum OrderStatus: string {
case Pending = 'pending';
case Processing = 'processing';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
}
Every developer on your team immediately understands the valid order statuses without hunting through documentation or database migrations.
Need to add a new status or rename an existing one? Update it once in the enum definition, and the change propagates everywhere in your application. This is vastly superior to searching for hardcoded strings across dozens of files.
Enums integrate seamlessly with Laravel's query builder and Eloquent ORM:
// Find all pending orders
$pendingOrders = Order::where('status', OrderStatus::Pending)->get();
// Using whereIn with multiple enum cases
$activeOrders = Order::whereIn('status', [
OrderStatus::Processing,
OrderStatus::Shipped,
])->get();
// Using Eloquent casts
class Order extends Model {
protected $casts = [
'status' => OrderStatus::class,
];
}
// Now $order->status is automatically an enum instance
$order = Order::first();
echo $order->status->value; // 'pending'
When you retrieve records from the database, Eloquent automatically converts string values to enum instances. This means you get type safety and IDE autocomplete throughout your application logic.
Enums aren't just containers for values—they can have methods. This lets you encapsulate related logic:
enum OrderStatus: string {
case Pending = 'pending';
case Processing = 'processing';
case Shipped = 'shipped';
case Delivered = 'delivered';
case Cancelled = 'cancelled';
public function isComplete(): bool {
return $this === self::Delivered;
}
public function isPending(): bool {
return $this === self::Pending;
}
public function canBeCancelled(): bool {
return $this !== self::Delivered && $this !== self::Cancelled;
}
public function label(): string {
return match($this) {
self::Pending => 'Awaiting Processing',
self::Processing => 'Being Prepared',
self::Shipped => 'On Its Way',
self::Delivered => 'Delivered',
self::Cancelled => 'Order Cancelled',
};
}
}
// Usage in your application
$order->status->label(); // 'Awaiting Processing'
if ($order->status->canBeCancelled()) {
// Show cancel button
}
This approach keeps logic organized and prevents scattered conditional statements throughout your application.
Let's build a practical example that demonstrates multiple enum benefits:
// Define user roles with methods
enum UserRole: string {
case Admin = 'admin';
case Editor = 'editor';
case Subscriber = 'subscriber';
public function canEditPosts(): bool {
return $this === self::Admin || $this === self::Editor;
}
public function canApproveComments(): bool {
return $this === self::Admin;
}
public function label(): string {
return match($this) {
self::Admin => 'Administrator',
self::Editor => 'Content Editor',
self::Subscriber => 'Subscriber',
};
}
}
// Use in your User model
class User extends Model {
protected $casts = [
'role' => UserRole::class,
];
public function canPublish(): bool {
return $this->role->canEditPosts();
}
}
// Use in controllers
class PostController extends Controller {
public function store(StorePostRequest $request) {
if (!auth()->user()->role->canEditPosts()) {
abort(403, 'Unauthorized');
}
// Create post
}
}
// Query by enum values
$editors = User::where('role', UserRole::Editor)->get();
$admins = User::whereIn('role', [UserRole::Admin])->get();
Testing enums ensures your business logic remains reliable:
use App\Enums\UserRole;
use App\Models\User;
use Tests\TestCase;
class UserRoleTest extends TestCase {
public function test_admin_can_edit_posts(): void {
$this->assertTrue(UserRole::Admin->canEditPosts());
}
public function test_subscriber_cannot_edit_posts(): void {
$this->assertFalse(UserRole::Subscriber->canEditPosts());
}
public function test_admin_can_approve_comments(): void {
$this->assertTrue(UserRole::Admin->canApproveComments());
}
public function test_editor_cannot_approve_comments(): void {
$this->assertFalse(UserRole::Editor->canApproveComments());
}
public function test_user_role_is_cast_to_enum(): void {
$user = User::factory()->create(['role' => 'admin']);
$this->assertInstanceOf(UserRole::class, $user->role);
$this->assertEquals(UserRole::Admin, $user->role);
}
public function test_query_by_enum_value(): void {
User::factory()->create(['role' => UserRole::Admin->value]);
User::factory()->create(['role' => UserRole::Editor->value]);
$admins = User::where('role', UserRole::Admin)->get();
$this->assertCount(1, $admins);
$this->assertEquals(UserRole::Admin, $admins->first()->role);
}
}
Also test how enums work within your application's features:
public function test_only_editors_can_publish_posts(): void {
$admin = User::factory()->create(['role' => UserRole::Admin]);
$editor = User::factory()->create(['role' => UserRole::Editor]);
$subscriber = User::factory()->create(['role' => UserRole::Subscriber]);
$this->actingAs($editor)
->post('/posts', ['title' => 'Test', 'content' => 'Test'])
->assertSuccessful();
$this->actingAs($subscriber)
->post('/posts', ['title' => 'Test', 'content' => 'Test'])
->assertForbidden();
}
Always use strict equality (===) when comparing enum instances:
// Wrong - loose comparison can cause unexpected behavior
if ($status == OrderStatus::Pending) { }
// Correct
if ($status === OrderStatus::Pending) { }
Without the cast, retrieved values remain strings. Always add enums to your model's $casts array:
// Incomplete
class Order extends Model {
// Missing casts!
}
// Correct
class Order extends Model {
protected $casts = [
'status' => OrderStatus::class,
];
}
Take advantage of type safety by using enums in function signatures:
// Loses type safety
public function updateStatus($status) { }
// Type-safe
public function updateStatus(OrderStatus $status) { }
Laravel enums and MySQL/PostgreSQL ENUM columns solve related but different problems. Here's when to use each:
| Aspect | Laravel Enums | Database ENUM |
|---|---|---|
| Type Safety | Yes, in PHP code | Yes, at database level |
| Methods & Logic | Full support | Not possible |
| Portability | Works across databases | Database-specific |
| Refactoring | Easy via migrations | Complex alterations |
| Best For | Application logic, methods, refactoring | Strict database constraints |
Recommendation for junior developers: Start with Laravel enums for type safety and flexibility. Use database ENUMs only if you need strict database-level validation that prevents invalid values from being stored under any circumstances.
To use enums in Laravel, ensure you meet these minimum requirements:
If you're running older versions, consider upgrading as part of your development roadmap. Modern Laravel development increasingly assumes enum availability.
As a junior developer, mastering enums is one of the highest-ROI skills you can develop. They eliminate entire categories of bugs, make your code more maintainable, and align you with modern Laravel best practices. The five key benefits we've covered—eliminating magic strings, achieving type safety, improving readability, simplifying queries, and adding functionality—all contribute to writing more professional, reliable applications.
Start incorporating enums into your next Laravel project. Begin with simple enums for user roles or order statuses, write tests for your enum logic, and gradually explore more advanced patterns. Your future self (and your team) will thank you for the cleaner, safer code.