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

Enum Bundle Laravel Package

fervo/enum-bundle

fervo/enum-bundle provides practical utilities for working with PHP enums in Laravel applications, including helpers for enum-backed model attributes, validation, and common conversions so enum values stay type-safe and easy to use throughout your codebase.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and register

composer require fervo/enum-bundle

There is almost certainly no Symfony Flex recipe, so register it manually:

// config/bundles.php
return [
    // ...
    Fervo\EnumBundle\FervoEnumBundle::class => ['all' => true],
];

2. Define your first enum

The bundle centers on a base class you extend with class constants:

namespace App\Enum;

use Fervo\EnumBundle\Enum\Enum;

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

3. First use case: a form field backed by the enum

use App\Enum\OrderStatus;
use Fervo\EnumBundle\Form\Type\EnumType;

$builder->add('status', EnumType::class, [
    'enum_class' => OrderStatus::class,
]);

Where to look first

  • The base Enum class — that's where getValues(), getChoices(), isValid() and friends live; everything else builds on it.
  • The Doctrine type — decide early whether you're storing the scalar value in a string column or not.
  • The Validator constraint — the cheapest way to harden request payloads.

Before you commit: this is an 11-star package with a last release in October 2022 and zero dependents. If you are on PHP 8.1+, native backed enums plus Symfony 6.2's EnumType/BackedEnumType and Doctrine's enumType cover nearly all of this. Adopt this bundle when you're maintaining a Symfony 4/5 or PHP 7.4 codebase and want the helper API, not as a greenfield dependency.

Implementation Patterns

Enum definition conventions

Use string constants for anything crossing a database or API boundary, and treat the constant as the single source of truth in entities, queries, and forms:

$qb->andWhere('o.status = :status')
   ->setParameter('status', OrderStatus::PAID); // never type 'paid' inline

Derive form choices instead of duplicating them:

'choices' => OrderStatus::getChoices(),

If labels need to be human-readable, override with choice_label rather than baking display strings into the constants.

Doctrine mapping

Create a thin type per enum and register it globally:

namespace App\Doctrine;

use App\Enum\OrderStatus;
use Fervo\EnumBundle\Doctrine\Types\EnumType;

class OrderStatusType extends EnumType
{
    public function getName(): string
    {
        return 'order_status';
    }

    protected function getEnumClass(): string
    {
        return OrderStatus::class;
    }
}
# config/packages/doctrine.yaml
doctrine:
  dbal:
    types:
      order_status: App\Doctrine\OrderStatusType

Then #[ORM\Column(type: 'order_status')]. One type per enum, type names prefixed — Doctrine type names share one global registry.

Validation at the edges

Put the constraint on DTOs and command objects, not just entities, so invalid values fail before they reach the domain:

use Fervo\EnumBundle\Validator\Constraints\Enum;

class UpdateOrderRequest
{
    #[Enum(enumClass: OrderStatus::class)]
    public string $status;
}

This is the pattern that pays for itself: controllers and message handlers can assume the value is valid, and the constraint produces a standard Symfony violation rather than a 500 from the Doctrine type.

Hydration from requests and APIs

Convert strings to enum-backed values at the boundary and return 400 on failure:

if (!OrderStatus::isValid($input)) {
    throw new BadRequestHttpException('Unknown status');
}

For JSON, serialize the scalar value (the constant), not the enum object — it keeps your payloads stable if you later migrate to native enums.

Twig rendering

Expose the choices once as a Twig global or via the bundle's extension, then render labels from a single map. Avoid {% if status == 'paid' %} chains; compare against constant('App\\Enum\\OrderStatus::PAID').

Gotchas and Tips

The native-enum collision is the big one. If you're on PHP 8.1+, Symfony ships Symfony\Component\Form\Extension\Core\Type\EnumType. Importing both is a fatal name clash — alias one (use ... as FervoEnumType;) or, better, don't mix the two philosophies in one codebase.

Missing type registration is the #1 runtime error. If you see Unknown column type "order_status" requested, the type wasn't registered for the environment (check doctrine.dbal.types and that the bundle is enabled in that env). It surfaces at schema-tool time and at query time, which makes it look like two different bugs.

Removing an enum constant is a data migration. Rows containing a retired value will throw when the Doctrine type converts them back. Before dropping a constant, run a data migration or make the type lenient (return null / a fallback for unknown values). Test this on a production-sized dataset — it's the kind of thing that only breaks on real data.

Values are not type-safe. Constants are string/int at the PHP level, so passing 'pendng' compiles fine. Add static analysis (@psalm-type or a PHPStan enum rule) or lean on the validator constraint. If you later move to native backed enums, ::from() gives you that safety for free — a good reason to keep the conversion boundary thin now.

Case sensitivity and strict comparison. Comparisons are strict string comparisons; 'Paid' is not 'paid'. Normalize input in the DTO (strtolower) before validating, or you'll chase phantom validation failures.

getChoices() uses the value as both key and label. That's fine for a select, but if you need translated labels, register a custom choice_label callback rather than adding display text to constants (which then leaks into the database).

Extension points worth knowing: subclass the Doctrine type for custom null handling or default values; add a Form type extension to attach constraints automatically; write a Twig function that wraps getChoices() so templates never touch the enum class name directly.

Debugging checklist: var_dump(OrderStatus::getValues()) to confirm the constant set, OrderStatus::isValid($x) to confirm boundary logic, and bin/console doctrine:schema:validate after any type change. In tests, assert with assertSame(OrderStatus::PAID, $entity->getStatus()) — comparing loosely against a raw string hides conversion bugs.

Have an exit plan. Given the low activity, treat this as a thin adapter layer: keep enum classes in App\Enum, keep Doctrine types in App\Doctrine, and avoid calling bundle-specific statics from deep inside your domain. Swapping to native enums later then becomes a mechanical change to two directories instead of a rewrite.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect