PHP introduced native Enums in PHP 8.1. Laravel later added strong support for Enums and enhanced that support with additional features.
What are Enums?
Enum is a special class that represents a fixed set of constants.
For example, Before we had to use custom classes to manage different states or types.
// Using class
class PostStatus
{
public const DRAFT = 'draft';
public const PENDING = 'pending';
public const PUBLISHED = 'published';
public const ARCHIVED = 'archived';
}
// Using Enums
enum PostStatus: string
{
case Draft = 'draft';
case Pending = 'pending';
case Published = 'published';
case Archived = 'archived';
}
// Enum Usage
PostStatus::Draft->value; // draft
PostStatus::Draft->name; // Draft
Advantages of Enums
Enums has more advantages than using custom classes to manage different states or types. Some of the advantages are:
- Type Safety: Enum is own type that provides runtime type checking.
- Auto-completion: For Enums IDEs provides auto-completion for better developer experience. It also provides better code readability.
- Method Support: Enums can have methods, which allows you to add custom logic to the enum.
- Immutability: Enum cases are singleton instances. They don't have mutable properties.
Types of Enums
1. Backed Enums
Backed Enums are Enums that have a value of type string or int.
enum Status: string
{
case Draft = 'draft';
case Pending = 'pending';
case Published = 'published';
case Archived = 'archived';
}2. Pure Enums
Pure Enums are Enums that do not have a value.
enum Status
{
case Draft;
case Pending;
case Published;
case Archived;
}Using Enum in Laravel
Create Enum
php artisan make:enum PostStatusThis will create an Enum in the app/Enums directory.
Using Enum in Model
This is one of the most important uses of Enums in Laravel. Instead of using strings or integers to represent different states or types, we can use Enums.
protected $casts = [
'status' => PostStatus::class,
];Using Enum in Controller
Use Enums in Controller similar to using custom classes.
class PostController extends Controller
{
public function index()
{
$posts = Post::where('status', PostStatus::Draft)->get();
return view('posts.index', compact('posts'));
}
}Using Enum in View
Access Enums directly in the view if casted in the model.
@foreach ($posts as $post)
<p>{{ $post->status?->value }}</p>
<p>{{ $post->status?->name }}</p>
<p>{{ $post->status?->label() }}</p>
@endforeachSelect Options
Enums provide cases() method that returns an array of all the cases in the enum.
enum PostStatus: string
{
case Draft = 'draft';
case Pending = 'pending';
case Published = 'published';
case Archived = 'archived';
public function label(): string
{
return match ($this) {
self::Draft => 'Draft',
self::Pending => 'Pending',
self::Published => 'Published',
self::Archived => 'Archived',
};
}
}
// In model
class Post extends Model {
protected $casts = [
'status' => PostStatus::class,
];
}
<select name="status" id="status">
@foreach (PostStatus::cases() as $status)
<option value="{{ $status->value }}" {{ $post->status === $status ? 'selected' : '' }}>{{ $status->label() }}</option>
@endforeach
</select>In Validation & Value Retrieval
You can also use Enums in validation to validate the enum values and convert to the enum instance.
{
"status": "draft"
}use Illuminate\Validation\Rule;
// validate the enum
$request->validate([
'status' => ['required', Rule::enum(PostStatus::class)],
]);
// Get the validated value and convert into Enum instance
$status = $request->enum('status', PostStatus::class);In Migrations
Enums can also be used in migrations to set default values.
Use backed enum with Eloquent to ensure that the database schema matches the enum values.
enum PostStatus: string
{
case Draft = 'draft';
case Pending = 'pending';
case Published = 'published';
case Archived = 'archived';
public static function values(): array
{
return array_column(self::cases(), 'value');
}
}
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('status')->default(PostStatus::Draft->value); // stores enum value
$table->timestamps();
});
For restrict the column to enum. But this requires database schema changes every time when the enum case changes.
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->enum('status', PostStatus::values())->default(PostStatus::Draft->value);
$table->timestamps();
});Methods in Enum
You can also add methods to Enums to add custom logic to the enum.
enum PostStatus: int
{
case Draft = 0;
case Pending = 1;
case Published = 2;
case Archived = 3;
public function label(): string
{
return match ($this) {
self::Draft => 'Draft',
self::Pending => 'Pending',
self::Published => 'Published',
self::Archived => 'Archived',
};
}
}
// Usage
PostStatus::Draft->label(); // Draftfrom() vs tryFrom()
Backed Enums gives two useful methods from() and tryFrom().
// Using from()
PostStatus::from(0); // PostStatus::Draft
// Using tryFrom()
PostStatus::tryFrom(0); // PostStatus::Draft
// Using tryFrom() with invalid value
PostStatus::tryFrom(4); // null
// Using from() with invalid value
PostStatus::from(4); // ValueErrorcases() method
This method returns an array of all the cases in the enum.
enum PostStatus: int
{
case Draft = 0;
case Pending = 1;
case Published = 2;
case Archived = 3;
public function label(): string
{
return match ($this) {
self::Draft => 'Draft',
self::Pending => 'Pending',
self::Published => 'Published',
self::Archived => 'Archived',
};
}
}
$cases = PostStatus::cases();
foreach ($cases as $case) {
echo $case->value; // 0, 1, 2, 3
echo $case->label(); // Draft, Pending, Published, Archived
}Serialization of Enums
serialize() / unserialize()
PHP has serialize/unserialize support for enums.
enum PostStatus: int
{
case Draft = 0;
case Pending = 1;
case Published = 2;
case Archived = 3;
}
$serialized = serialize(PostStatus::Draft); // string
$unserialized = unserialize($serialized); // PostStatus::Draft
PostStatus::Draft === $unserialized; // truejsonSerialize()
Enums can also be serialized to JSON using built in JsonSerializable interface.
use JsonSerializable;
enum PostStatus: int implements JsonSerializable
{
case Draft = 0;
case Pending = 1;
case Published = 2;
case Archived = 3;
public function jsonSerialize(): mixed
{
return [
'value' => $this->value,
'name' => $this->name,
];
}
}
// With jsonSerialize
$serialized = json_encode(PostStatus::Draft);
// "{"value":0,"name":"Draft"}"
// Without jsonSerialize
$serialized = json_encode(PostStatus::Draft);
// "0" (It returns the value of the enum)Translatable Enums
If you are working with multiple languages, you can translate with enums.
Language File:
// lang/en/post-status.php
return [
'draft' => 'Draft',
'pending' => 'Pending',
'published' => 'Published',
'archived' => 'Archived'
];
enum PostStatus: string
{
case Draft = "draft";
case Pending = "pending";
case Published = "published";
case Archived = "archived";
public function label(): string
{
return __("post-status.{$this->value}");
}
}
// Usage
PostStatus::Draft->label(); // Draftconst in enums
enum PostStatus: string
{
case DRAFT = "draft";
case PENDING = "pending";
case PUBLISHED = "published";
case ARCHIVED = "archived";
public const DRAFT_STATUS = self::DRAFT->value;
}
// Usage
PostStatus::DRAFT_STATUS;static method in Enum
enum PostStatus: int
{
case Draft = 0;
case Pending = 1;
case Published = 2;
case Archived = 3;
public static function options(): array
{
return array_column(self::cases(), 'name', 'value');
}
}
// Usage
PostStatus::options(); // [0 => "Draft", 1 => "Pending", 2 => "Published", 3 => "Archived"]Advance Enum Features
Interfaces in Enums
You can implement any number of interfaces in enums.
interface HasLabel {
public function label(): string;
}
enum PostStatus: string implements HasLabel
{
case DRAFT = "draft";
case PENDING = "pending";
case PUBLISHED = "published";
case ARCHIVED = "archived";
public function label(): string
{
return match ($this) {
self::DRAFT => 'Draft',
self::PENDING => 'Pending',
self::PUBLISHED => 'Published',
self::ARCHIVED => 'Archived',
};
}
}Traits in Enums
trait HasCommonMethods {
// works only for backed enums
public static function values() : array {
return array_column(self::cases(), 'value');
}
// works for both backed and pure enums
public static function names(): array {
return array_column(self::cases(), 'name');
}
}
enum PostStatus: string {
use HasCommonMethods;
case DRAFT = 'draft';
case PENDING = 'pending';
case PUBLISHED = 'published';
case ARCHIVED = 'archived';
}
// Usage
PostStatus::values(); // ["draft", "pending", "published", "archived"]
PostStatus::names(); // ["DRAFT", "PENDING", "PUBLISHED", "ARCHIVED"]Using AsEnumCollection and AsEnumArrayObject
When database column contains multiple values of enum , use AsEnumCollection and AsEnumArrayObject.
suppose database column statuses that contains [1,2]
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
enum PostStatus :int {
case DRAFT = 1;
case PENDING = 2;
case PUBLISHED = 3;
case ARCHIVED = 4;
}AsEnumCollection
// In model
class Post extends Model {
protected $casts = [
'statuses' => AsEnumCollection::of(PostStatus::class),
];
}
$post = Post::find(1);
$post->statuses; // collect([PostStatus::DRAFT, PostStatus::PENDING])
$post->statuses->contains(PostStatus::DRAFT);
$post->statuses->each(function(PostStatus $status){
echo $status->name; // DRAFT , PENDING
echo $status->value; // 1 , 2
});
AsEnumArrayObject
This is similar to AsEnumCollection but it returns an ArrayObject instead of a Collection.
// In model
class Post extends Model {
protected $casts = [
'statuses' => AsEnumArrayObject::of(PostStatus::class),
];
}
$post = Post::find(1);
$post->statuses; // ArrayObject([PostStatus::DRAFT, PostStatus::PENDING])
$post->statuses[0]; // PostStatus::DRAFT
$post->statuses[] = PostStatus::ARCHIVED;
$post->statuses; // ArrayObject([PostStatus::DRAFT, PostStatus::PENDING, PostStatus::ARCHIVED])
$post->save();Diffrence From Object
Enums are built on classes and objects, but still they do not support all object-related features.
- Constructors and Destructors are not allowed.
- Inheritance is not supported but may implement any number of interfaces.
- static and instance properties not allowed.
- Enum Case must be singleton instances.
- Enums must be used after declaring them.
- Public, private, and protected methods, static methods, and constants are allowed.
Conclusion
Enums are a great way to manage different states or types in your application. They provide type safety, auto-completion, and method support. They are also immutable, which makes them a good choice for managing different states or types in your application.