mhujer/consistence-bundle
Symfony bundle integrating the Consistence library, adding strict typing, enums, and value object support to your Symfony app. Provides service and Doctrine integration for cleaner domain models.
This bundle is thin glue between consistence/consistence enums and two Symfony subsystems: the Form component (choice fields backed by an enum class) and the Translator (turning enum values into human labels). It is not a full enum toolkit — no Doctrine type, no serializer, no validator — so treat it as boilerplate removal, not a framework.
composer require mhujer/consistence-bundle consistence/consistence
config/bundles.php:return [
// ...
Mhujer\ConsistenceBundle\ConsistenceBundle::class => ['all' => true],
];
Look here first: src/Form/Type/ for the form types and src/Translator/ for the translator, then Resources/config/services.xml (or config/services.php) for the exact service IDs and constructor arguments. The README is short; the source is the real documentation. Confirm the two things you will depend on daily: the enum form type's class name and the option name it expects for the enum class (class, enum_class, or similar).
First use case — an enum-backed <select>:
// src/Enum/OrderStatus.php
use Consistence\Enum\Enum;
class OrderStatus extends Enum
{
public const NEW = 'new';
public const SHIPPED = 'shipped';
public const CANCELLED = 'cancelled';
public static function getAvailableValues(): array
{
return [self::NEW, self::SHIPPED, self::CANCELLED];
}
}
$form = $this->createFormBuilder($order)
->add('status', \Mhujer\ConsistenceBundle\Form\Type\EnumType::class, [
'class' => OrderStatus::class, // verify option name in src/Form/Type
])
->getForm();
After submit, $order->getStatus() should be an OrderStatus instance, not a string.
Form type per enum, or one reusable type. Prefer a thin custom type wrapping the bundle's type so the enum class and labels live in one place:
class OrderStatusType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->add('status', EnumType::class, [
'class' => OrderStatus::class,
'choice_label' => fn (OrderStatus $s) => 'order_status.' . $s->getValue(),
'choice_translation_domain' => 'enums',
]);
}
}
Translate at the edge. Keep enum values as machine strings and translate only in presentation. If the bundle's translator decorates Symfony's, trans() accepts an enum instance; if not, use the convention explicitly:
{{ ('order_status.' ~ order.status.getValue())|trans({}, 'enums') }}
# translations/enums.en.yaml
order_status:
new: 'New'
shipped: 'Shipped'
cancelled: 'Cancelled'
Handle the null/empty case in the data transformer. Symfony will hand you '' for an empty choice; new OrderStatus('') will throw. Map empty input to null and make the entity property nullable. Add 'placeholder' => '— choose —' or 'required' => false deliberately.
Persistence stays your job. Store the scalar ($enum->getValue()) in a string column; convert in the entity getter/setter. The bundle does not ship a Doctrine type — do not assume enum exists in DBAL. Same for JSON APIs: add a normalizer that calls getValue(), otherwise the enum object serializes as an empty object.
Multi-select and enum sets. If the bundle exposes a set/multi form type, use multiple => true; check src/Form/Type for whether it returns an EnumSet/MultiEnum rather than an array of enums, and test the transformer round-trip.
Reuse the translator outside forms. Inject the bundle's translator into mailers/console commands so enum labels are consistent across HTTP, CLI, and email — that is where the bundle earns its keep.
Test the round trip, not just the rendering: assert the choice list equals OrderStatus::getAvailableValues(), then submit 'shipped' and assert the model data is OrderStatus::SHIPPED.
src/ for the tag you install.getAvailableValues() is the source of truth. Choice order follows its order; a missing or wrong implementation surfaces as an empty or exception-throwing field, not a validation error.choices, the bundle's transformer (which expects enum instances) will misbehave. Let the type build choices from the enum class.null is not a valid enum value. Every transformer needs an explicit empty-input branch; this is the most common runtime failure with this kind of glue code.cache:clear; missing keys fall back to raw values (new, shipped), which is easy to miss in dev.enums) to avoid collisions with generic keys like new or cancelled.Choice constraint over OrderStatus::getAvailableValues(), or a custom constraint checking isValid(), since the type alone does not guarantee server-side safety.services.yaml.ChoiceType + a transformer may be safer; if you have dozens of Consistence enums across a large app, the bundle's consistent labels and form wiring are worth pinning to a tagged release.How can I help you explore Laravel packages today?