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

Enumeration Laravel Package

eloquent/enumeration

Eloquent Enumeration brings typed enum support to Laravel’s Eloquent models. Define reusable enums, cast attributes to enum instances, and keep status or type fields type-safe and expressive without leaving familiar Eloquent conventions.

Deep Wiki
Context7

Getting Started

eloquent/enumeration is a plain-PHP enumeration library — no service provider, no facade, no Laravel coupling. Install it and start typing constants.

composer require eloquent/enumeration

First places to look:

  1. README.md for the AbstractEnumeration / Multiton split.
  2. src/AbstractEnumeration.php — the class you will extend in ~90% of cases.
  3. src/Multiton.php / src/AbstractMultiton.php — only if you need non-scalar member values.

The canonical first use case is a status column. In app/Enums/OrderStatus.php:

namespace App\Enums;

use Eloquent\Enumeration\AbstractEnumeration;

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

You then get both the raw constant (OrderStatus::PAID === 'paid') and a singleton instance via the magic static call (OrderStatus::PAID()). The instance exposes value(), key(), __toString(), is(), isNot(), compareTo(), plus class-level helpers such as members(), values(), keys(), valueOf(), valueOfKey(), isValidValue(), and isValidKey(). Confirm the exact helper list against the README of the version you install — it shifted between 5.x and 6.x.

A good first refactor: find every const STATUS_PAID = 'paid' or bare 'paid' string in models/services and move it into one enum class per domain concept.

Implementation Patterns

Custom Eloquent cast (Laravel 7+). The package predates CastsAttributes, so write a thin adapter once and reuse it:

class EnumerationCast implements CastsAttributes
{
    public function __construct(protected string $enumClass) {}

    public function get($model, $key, $value, $attributes)
    {
        return $value === null ? null : $this->enumClass::valueOf($value);
    }

    public function set($model, $key, $value, $attributes)
    {
        if ($value === null) return null;
        return $value instanceof $this->enumClass ? $value->value() : (string) $value;
    }
}

// In the model:
protected $casts = ['status' => EnumerationCast::class.':'.OrderStatus::class];

This is the single highest-leverage integration: attributes come back as enum objects, assignments accept either the object or the scalar, and the database still sees a string.

Validation. Wrap isValidValue() in a rule object so requests never carry string arrays:

class EnumerationRule implements ValidationRule
{
    public function __construct(private string $enumClass) {}

    public function validate($attribute, $value, $fail): void
    {
        if (!$this->enumClass::isValidValue($value)) {
            $fail("The {$attribute} is not a valid {$this->enumClass}.");
        }
    }
}

Use Rule::in(OrderStatus::values()) for the simple in: case, and the rule object when you need a custom message.

Dropdowns and API resources. OrderStatus::members() gives you instances; array_map(fn ($m) => ['value' => $m->value(), 'label' => $m->key()], OrderStatus::members()) is your select-options payload. In API resources, always output ->value(), never the object.

Query building. Order::where('status', OrderStatus::PAID()->value()) inside a scope or repository. Prefer this over comparing to constants so refactors are single-source.

State transitions. is() / isNot() read much better than === chains in service classes:

if ($order->status->isNot(OrderStatus::PENDING())) {
    throw new DomainException('Only pending orders can be paid.');
}

compareTo() is useful when you deliberately order members in the class body (pending < paid < shipped) and want sortable semantics.

Multiton for rich values. If members carry more than a scalar (e.g., a currency with symbol and precision), extend AbstractMultiton and register members in the static initialize() hook. This is the main reason to choose this package over myclabs/php-enum: object-valued members with identity semantics.

Testing. Factories should accept the enum and let the cast flatten it: 'status' => OrderStatus::PAID(). In assertions, compare ->value() rather than the object, since the singleton guarantee only holds within one process.

Gotchas and Tips

  • The constant/method duality is the biggest tripwire. OrderStatus::PAID is the string 'paid'; OrderStatus::PAID() is the instance. Half your bugs will be a missing (). Enforce a convention across the team and grep for ::[A-Z_]+[^\(] in reviews.
  • Archived since 2018. There will be no fix for PHP 8.x edge cases. Audit for deprecations (strlen(null), implicit nullable parameters) before adopting on PHP 8.2+, and pin the version in composer.json.
  • Migrate to native enums eventually. PHP 8.1 enum OrderStatus: string covers the scalar case with better static analysis and IDE support. Keep the enum classes thin and the cast adapter central so the swap is mechanical. Keep this package only where you need Multiton's object values or you're on PHP < 8.1.
  • Serialization identity. Instances are singletons per process. Do not persist enum objects to cache, queues, or session without converting to ->value() first — the deserialized object may not be === to the canonical member, breaking is()-based comparisons downstream.
  • Eloquent $casts doesn't support the class directly. Assigning an enum object to a raw attribute lets PDO receive an object; anything without __toString in the binding path will error. Always go through the custom cast.
  • valueOf() throws. It raises UnexpectedValueException for unknown values. In request-handling paths use isValidValue() first or catch and convert to a 422; don't let it bubble into a 500.
  • Falsy member values. A member with value '0' or '' will trip naive if ($enum) checks. isValidValue() uses strict comparison, but your own code may not — compare with !== null.
  • Choose one base class. AbstractEnumeration for scalar members, AbstractMultiton for object members, and the trait variant only when your class already extends something else. Mixing paradigms in one codebase makes the casts and rules non-uniform.
  • No Laravel auto-discovery. There is nothing to register. If you want OrderStatus::options() or OrderStatus::labels(), add them as static helpers on a shared base enum class in your app — the package has no extension-point config.
  • Reserved-word member names. CLASS, FUNCTION, TRAIT and friends are legal as class constants in modern PHP but have caused keyword collisions in older parsers. If a member name reads like a keyword, rename the constant rather than fight the toolchain.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings