Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Enumerated Laravel Package

davidianbonner/enumerated

Enumerated brings first-class enum support to Laravel. Define backed enums and use them in Eloquent casts, validation rules, and form requests, with helpers for labels, options, and comparisons. Keeps domain values type-safe and readable.

View on GitHub
Deep Wiki
Context7

Getting Started

davidianbonner/enumerated is a thin base class that turns a plain PHP class full of const declarations into a queryable enumeration. v8.0.1 is a patch release — the notes are just a diff link between v8.0.0...v8.0.1, with no documented features, deprecations, or breaking changes. Treat ^8.0 as the current line; if you're coming from 7.x, read that diff rather than expecting a migration guide.

Minimal setup (about two minutes):

composer require davidianbonner/enumerated

There is nothing to publish and no service provider wiring to remember — it's a single base class plus a couple of traits. Your first stop should be vendor/davidianbonner/enumerated/src/Enum.php: it's short, and confirming the exact static method names (values(), keys(), toArray(), isValid(), fromValue(), fromKey()) against the source is faster than trusting a blog post.

Then create your first enum:

namespace App\Enums;

use DAB\Enumerated\Enum;

class OrderStatus extends Enum
{
    const PENDING   = 'pending';
    const PAID      = 'paid';
    const SHIPPED   = 'shipped';
    const CANCELLED = 'cancelled';
}

First real use case: replace magic strings in a model. Instead of $order->status = 'paid' scattered across controllers, jobs, and Blade views, you write OrderStatus::PAID, and you get OrderStatus::values() for validation and OrderStatus::toArray() for a select box for free. That single swap — status column plus a form — is where the package pays for itself.

Implementation Patterns

Validation. The most common daily use is feeding the value list into a rule:

$request->validate([
    'status' => ['required', Rule::in(OrderStatus::values())],
]);

Wrap it in a custom rule class once you have more than three enums, so the Rule::in(...) boilerplate doesn't spread.

Eloquent casting. Store the scalar (string or int) in the column and translate at the boundary. A custom cast keeps models clean:

class OrderStatusCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
        return OrderStatus::fromValue($value); // returns the constant *name*
    }

    public function set($model, $key, $value, $attributes)
    {
        return [$key => $value];
    }
}

If the package's own accessor trait fits your version, prefer it — but confirm its behaviour on null before adopting it model-wide.

Form selects and Blade. OrderStatus::toArray() gives ['PENDING' => 'pending', ...], which is directly usable in Form::select-style helpers or @foreach. For display, prefer a label map over the raw constant name:

public static function labels(): array
{
    return [
        self::PENDING   => __('orders.status.pending'),
        self::PAID      => __('orders.status.paid'),
        self::SHIPPED   => __('orders.status.shipped'),
        self::CANCELLED => __('orders.status.cancelled'),
    ];
}

Because the enum is just a normal class, adding static helpers (labels(), badgeColor(), next()) is the intended extension point — there is no plugin system to learn.

Behaviour, not just data. The biggest win over raw constants is pairing the enum with match:

return match ($order->status) {
    OrderStatus::PENDING => 'Awaiting payment',
    OrderStatus::PAID    => 'Ready to ship',
    default              => 'Unknown',
};

Factories and seeders. fake()->randomElement(OrderStatus::values()) keeps test data in sync automatically, so adding a new status doesn't silently leave your seeders behind.

API resources. Expose both the machine value and the label — clients should never have to hardcode your constant names:

'status' => [
    'value' => $this->status,
    'label' => OrderStatus::labels()[$this->status] ?? $this->status,
],

Integration points to standardise on: one enum class per domain concept, a labels() map, a form request that validates with Rule::in(...), and a cast on the model. Once those four are in place, a new status is a one-line constant plus a label — and everything else follows.

Gotchas and Tips

  • values() follows declaration order. It uses reflection over the class constants, so reordering constants changes array order. Never rely on positional/index logic against values().
  • Duplicate values silently break lookups. Two constants with the same scalar will make fromValue() ambiguous — it returns the first match. Keep values unique, or write a test that asserts count(values()) === count(array_unique(values())).
  • Loose comparisons. isValid() and in_array can surprise you with '1' vs 1, 0 vs '', or null. Cast request input before validating, and guard null explicitly rather than assuming isValid(null) is false.
  • Renaming or removing a constant is a data migration, not a refactor. Existing rows still hold the old value. Ship a backfill migration and a deprecation window; treat it exactly like changing a database column.
  • Don't serialize enum instances. There's no state to carry — store and transport the scalar, and resolve the constant name at render time. This keeps queues and API payloads versioning-friendly.
  • Keep the DB representation scalar. If you switch a column to a native DB ENUM, the migration and the PHP class can drift. A string column plus application-level validation is usually the lower-maintenance choice.
  • IDE and static analysis. values() returns array, so completion is lost. Add @return array<string>-style docblocks or PHPStan type aliases on your subclasses; it's cheap and pays back immediately.
  • Long-running workers (Octane, queue daemons). Constants are compile-time, so this is safe — but if you hot-edit enum files, restart workers before debugging "stale" values, or you'll chase ghosts.
  • composer dump-autoload after adding a new enum namespace. PSR-4 misses are the single most common "class not found" cause here.
  • Debugging one-liner: dd(OrderStatus::toArray(), OrderStatus::keys()) tells you instantly whether a value mismatch is in your code or your data.
  • Patch-level hygiene for 8.0.1. Since the release is maintenance-only, pin ^8.0, let CI prove the bump, and review the diff only if you have anything subclassing the base Enum or relying on its internals. That's the one place a patch-level change can reach you.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle