Laravel Typed Enums: Casts, Rules &amp; PHP 8.3 Patterns | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Laravel Typed Enums as First-Class Citizens: Casts, Rules, and PHP 8.3 Features        On this page       1. [  Why Enums Deserve More Than a CastsAttributes Afterthought ](#why-enums-deserve-more-than-a-codecastsattributescode-afterthought)
2. [  1. Backed Enums as Eloquent Casts ](#1-backed-enums-as-eloquent-casts)
3. [  2. Enum-Aware Validation Rules ](#2-enum-aware-validation-rules)
4. [  3. Route Model Binding with Enums ](#3-route-model-binding-with-enums)
5. [  4. PHP 8.3 Typed Class Constants ](#4-php-83-typed-class-constants)
6. [  5. Serialising Enums in API Resources ](#5-serialising-enums-in-api-resources)
7. [  Takeaways ](#takeaways)

  ![Laravel Typed Enums as First-Class Citizens: Casts, Rules, and PHP 8.3 Features](https://cdn.msaied.com/483/0b80ca4c39b98b48f8d3475ac949ea0f.png)

  #laravel   #php   #enums   #eloquent   #api  

 Laravel Typed Enums as First-Class Citizens: Casts, Rules, and PHP 8.3 Features 
=================================================================================

     29 Jul 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said  

       Table of contents

1. [  01   Why Enums Deserve More Than a CastsAttributes Afterthought  ](#why-enums-deserve-more-than-a-codecastsattributescode-afterthought)
2. [  02   1. Backed Enums as Eloquent Casts  ](#1-backed-enums-as-eloquent-casts)
3. [  03   2. Enum-Aware Validation Rules  ](#2-enum-aware-validation-rules)
4. [  04   3. Route Model Binding with Enums  ](#3-route-model-binding-with-enums)
5. [  05   4. PHP 8.3 Typed Class Constants  ](#4-php-83-typed-class-constants)
6. [  06   5. Serialising Enums in API Resources  ](#5-serialising-enums-in-api-resources)
7. [  07   Takeaways  ](#takeaways)

 Why Enums Deserve More Than a `CastsAttributes` Afterthought
------------------------------------------------------------

Most teams adopt backed enums, slap `->casts` on the model, and call it done. That leaves a lot of safety on the table. Enums can own their own validation logic, drive route resolution, and — with PHP 8.3 typed class constants — become genuinely self-documenting domain primitives.

---

1. Backed Enums as Eloquent Casts
---------------------------------

Laravel resolves any `BackedEnum` class directly in the `$casts` array:

```php
// app/Enums/OrderStatus.php
enum OrderStatus: string
{
    case Pending   = 'pending';
    case Confirmed = 'confirmed';
    case Shipped   = 'shipped';
    case Cancelled = 'cancelled';

    public function label(): string
    {
        return match($this) {
            self::Pending   => 'Awaiting Payment',
            self::Confirmed => 'Order Confirmed',
            self::Shipped   => 'On Its Way',
            self::Cancelled => 'Cancelled',
        };
    }

    public function isTerminal(): bool
    {
        return in_array($this, [self::Shipped, self::Cancelled], true);
    }
}

```

```php
// app/Models/Order.php
protected $casts = [
    'status' => OrderStatus::class,
];

```

Now `$order->status` is always an `OrderStatus` instance — never a raw string. Calling `$order->status->label()` is safe everywhere without defensive checks.

---

2. Enum-Aware Validation Rules
------------------------------

Laravel ships `Rule::enum()`, but you can push logic into the enum itself for reuse across HTTP and CLI contexts:

```php
use Illuminate\Validation\Rules\Enum;

// In a Form Request
public function rules(): array
{
    return [
        'status' => ['required', new Enum(OrderStatus::class)],
    ];
}

```

For transitions, a custom rule keeps the policy inside the enum:

```php
enum OrderStatus: string
{
    // ... cases above ...

    /** @return self[] */
    public function allowedTransitions(): array
    {
        return match($this) {
            self::Pending   => [self::Confirmed, self::Cancelled],
            self::Confirmed => [self::Shipped,   self::Cancelled],
            default         => [],
        };
    }
}

```

```php
// app/Rules/ValidStatusTransition.php
class ValidStatusTransition implements ValidationRule
{
    public function __construct(private readonly OrderStatus $current) {}

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $next = OrderStatus::tryFrom($value);

        if ($next === null || ! in_array($next, $this->current->allowedTransitions(), true)) {
            $fail("Cannot transition from {$this->current->value} to {$value}.");
        }
    }
}

```

The rule is instantiated with the *current* status, so the transition matrix lives in one place.

---

3. Route Model Binding with Enums
---------------------------------

Bind an enum directly in a route without a custom resolver:

```php
// routes/api.php
Route::get('/orders/status/{status}', [OrderController::class, 'byStatus']);

```

```php
// app/Http/Controllers/OrderController.php
public function byStatus(OrderStatus $status): JsonResponse
{
    $orders = Order::where('status', $status)->paginate();
    return response()->json($orders);
}

```

Laravel automatically calls `OrderStatus::from($routeValue)` and returns a 404 if the value is invalid — zero boilerplate.

---

4. PHP 8.3 Typed Class Constants
--------------------------------

PHP 8.3 allows typed constants on enums, which is perfect for associating metadata without a separate config file:

```php
enum OrderStatus: string
{
    case Pending   = 'pending';
    case Confirmed = 'confirmed';
    case Shipped   = 'shipped';
    case Cancelled = 'cancelled';

    // Typed constant — enforced by the engine
    const array TERMINAL = [self::Shipped, self::Cancelled];
    const string DEFAULT  = self::Pending->value;
}

```

Now `OrderStatus::TERMINAL` is a typed `array` constant — no docblock needed, and static analysis tools understand it without plugins.

---

5. Serialising Enums in API Resources
-------------------------------------

Avoid leaking raw values or accidentally serialising the enum object:

```php
// app/Http/Resources/OrderResource.php
public function toArray(Request $request): array
{
    return [
        'id'     => $this->id,
        'status' => [
            'value'      => $this->status->value,
            'label'      => $this->status->label(),
            'terminal'   => $this->status->isTerminal(),
        ],
    ];
}

```

Frontend consumers get a stable contract with both machine and human-readable representations.

---

Takeaways
---------

- Use `$casts` with the enum class directly — Laravel handles `BackedEnum` natively.
- Push transition logic into the enum itself; validation rules just delegate to it.
- Route model binding resolves backed enums automatically with a 404 on invalid values.
- PHP 8.3 typed constants let enums carry structured metadata without external config.
- Serialise enums explicitly in API resources to keep frontend contracts stable.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-typed-enums-as-first-class-citizens-casts-rules-and-php-83-features&text=Laravel+Typed+Enums+as+First-Class+Citizens%3A+Casts%2C+Rules%2C+and+PHP+8.3+Features) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Flaravel-typed-enums-as-first-class-citizens-casts-rules-and-php-83-features) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  Does Laravel automatically validate enum values when casting?        No. The cast silently returns null for invalid values when using `tryFrom` internally. You still need `Rule::enum()` or a custom validation rule in your Form Request to reject bad input before it reaches the model. 

      Q02  Can I use a pure (non-backed) enum as an Eloquent cast?        Not directly. Eloquent's built-in enum cast requires a `BackedEnum` because it needs a scalar value to store in the database. For pure enums you must implement a custom `CastsAttributes` class. 

      Q03  Are PHP 8.3 typed constants on enums supported by PHPStan and Psalm?        Yes. Both PHPStan (level 6+) and Psalm understand typed class constants on enums as of their current stable releases, giving you full static analysis coverage without extra stubs. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![Laravel Horizon: Queue Metrics, Supervisor Tuning, and Safe Deployments](https://cdn.msaied.com/660/4e7fb1097d66f5b5c6bb68e5aad9b211.png) laravel horizon queues 

### Laravel Horizon: Queue Metrics, Supervisor Tuning, and Safe Deployments

Beyond the dashboard: how to use Horizon's metrics API, tune supervisor processes for mixed workloads, and dep...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 12 Sep 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-horizon-queue-metrics-supervisor-tuning-and-safe-deployments) [ ![Custom Eloquent Casts: Value Objects, Enums, and Encrypted Composites](https://cdn.msaied.com/659/44f701e1dc43e64d0b7ecc984d0b34bc.png) laravel eloquent ddd 

### Custom Eloquent Casts: Value Objects, Enums, and Encrypted Composites

Go beyond primitive storage with Eloquent's CastsAttributes contract. Build reusable value-object casts, handl...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 12 Sep 2026     3 min read  

  Read    

 ](https://msaied.com/articles/custom-eloquent-casts-value-objects-enums-and-encrypted-composites) [ ![Laravel Macro-Free Extensibility: Custom Query Builder Classes and Fluent Scopes](https://cdn.msaied.com/658/b45c3cc06b92a332e526bed9bb1f826d.png) laravel eloquent architecture 

### Laravel Macro-Free Extensibility: Custom Query Builder Classes and Fluent Scopes

Skip global macros and reach for typed, testable custom query builder classes in Laravel. Learn how to bind a...

  ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said 

 11 Sep 2026     1 min read  

  Read    

 ](https://msaied.com/articles/laravel-macro-free-extensibility-custom-query-builder-classes-and-fluent-scopes) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
