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

lamoda/enum-bundle

Lamoda EnumBundle is a lightweight Symfony bundle for modeling enumerated values as typed PHP classes, with labels, validation, Doctrine DBAL mapping, and form choice integration, helping teams keep reusable enum definitions consistent across domain logic and UI in Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the bundle; it pulls in lamoda/php-enum transitively:

composer require lamoda/enum-bundle

As of 1.6 the bundle supports Symfony 6 (alongside the 4/5 lines it previously targeted), so it can be dropped into a current LTS app without a compatibility shim. Symfony Flex registers it automatically; otherwise add it to config/bundles.php:

return [
    // ...
    Lamoda\EnumBundle\LamodaEnumBundle::class => ['all' => true],
];

Define your first enum by extending the base Enum class and declaring constants:

namespace App\Enum;

use Lamoda\Enum\Enum;

final class OrderStatus extends Enum
{
    public const NEW     = 'new';
    public const PAID    = 'paid';
    public const SHIPPED = 'shipped';
}

Use it as a value object:

$status = new OrderStatus(OrderStatus::NEW);

$status->getValue();   // 'new'
OrderStatus::getValues(); // ['new', 'paid', 'shipped']
OrderStatus::isValid('paid'); // true

Where to look first: the bundle's form type (Lamoda\EnumBundle\Form\Type\EnumType) and its serializer normalizer service, since those are the two integration points you'll touch daily. The first realistic use case is replacing a plain string $status on an entity with an OrderStatus and wiring it into a form and the Symfony Serializer.

Implementation Patterns

Forms. Add the bundle's enum type wherever you previously used a ChoiceType with hard-coded choices:

use Lamoda\EnumBundle\Form\Type\EnumType;

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

The class option is required; the type derives the allowed choices from the enum's values. Override choice_label if you want human-readable labels instead of raw values.

Entities and serializer. Declare the property with the enum type and let the normalizer translate it:

class Order
{
    private OrderStatus $status;

    public function getStatus(): OrderStatus { return $this->status; }
    public function setStatus(OrderStatus $status): void { $this->status = $status; }
}

With the bundle enabled, serializing an Order emits "status": "paid" and denormalizing JSON back into Order constructs the enum from the scalar, as long as the target property/constructor parameter is typed OrderStatus. If you build a Serializer manually (outside the framework), register the bundle's enum normalizer explicitly and place it ahead of generic object normalizers:

$serializer = new Serializer([
    new EnumNormalizer(),
    new ObjectNormalizer(/* ... */),
]);

Doctrine. There is no automatic DBAL type; the common pattern is to persist the scalar and hydrate the object:

// storing
$order->setStatus(new OrderStatus(OrderStatus::PAID));
$conn->insert('orders', ['status' => $order->getStatus()->getValue()]);

// reading
$status = new OrderStatus($row['status']);

For query building, compare against getValue() strings rather than enum objects.

Controllers and templates. The form's getData() returns your entity with a real enum instance, so a standard flow works unchanged:

$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
    $order = $form->getData(); // $order->getStatus() is an OrderStatus
    $this->em->persist($order);
}

In Twig, either use __toString() ({{ order.status }}) or access the scalar ({{ order.status.value }}), the latter being safer for output escaping and comparisons.

Validation. Use the static isValid() when validating raw input (e.g., query parameters not going through a form), or write a small custom constraint that delegates to it.

Gotchas and Tips

  • Symfony 6 support arrived in 1.6, but nothing else changed. The release is a constraint bump only — no API, form-option, or normalizer changes. Still run your form and serializer tests after upgrading, since Symfony 6 removed several deprecated form/serializer behaviours your app may have been leaning on elsewhere. Symfony 6 also means PHP 8.0+ (8.1+ for most apps), so double-check your composer.json platform constraints line up.
  • Enums are objects, not strings. === compares instance identity, so two OrderStatus('paid') objects are not identical. Compare getValue() or use the enum's equals() helper where available. Never compare an enum to a raw string.
  • Nullability bites. A typed property OrderStatus $status cannot hold null. Use ?OrderStatus for optional fields, and make sure Doctrine hydrators and form data mappers handle the null case.
  • The class option is mandatory on EnumType. Forgetting it produces a confusing form-building error rather than a "missing option" message.
  • Normalizer ordering matters. If another normalizer (or default object handling) runs first, enums can be serialized as objects/arrays instead of scalars. Verify registration with bin/console debug:container and, if needed, reorder or decorate.
  • Denormalization needs type information. Scalars back to enums only work when the target is typed; untyped arrays or mixed will stay strings. For DTOs, type constructor parameters explicitly.
  • No magic Doctrine mapping. You own the conversion in both directions. Store in a string column (or register a custom DBAL type) and construct the enum on hydrate; wrap construction so invalid legacy data surfaces as a clear error, not a raw constructor exception.
  • Don't mix with native PHP 8.1 enums. This bundle targets class-constant-based enums extending Lamoda\Enum\Enum. The enum keyword produces a different construct and won't be picked up by the form type or normalizer. This is the sharpest edge now that Symfony 6 apps are typically on PHP 8.1+ — the language feature is right there, but it is not interchangeable with this package.
  • Cache after config changes. Adding the bundle, changing service configuration, or bumping the package requires bin/console cache:clear; stale containers are a frequent source of "service not found" confusion — particularly after a version bump where the container was compiled against the old constraint set.
  • Extension points: subclass Enum for shared behavior, override form options in a custom type, decorate the normalizer for custom scalar formats, or add a DBAL type if you want transparent Doctrine mapping.
  • Maintenance risk. The 1.6 release resolves the main blocker (Symfony 6 compatibility), which is a genuine positive signal, but it is a one-line constraint bump with low stars/dependents and no broader feature work. Keep composer.lock pinned and isolate usages behind thin adapters (form type wrappers, a serialization layer) so migrating to native enums or myclabs/php-enum later is a contained change.
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