ruwork/polyfill-form-dti
A lightweight compatibility polyfill that brings DateTimeImmutable support to Symfony Form date and time types on older framework versions, letting you use immutable date/time form fields without upgrading Symfony.
This is a Symfony Form polyfill, not a Laravel package, so the first decision is whether you need Symfony's Form component at all. In a normal Laravel app you don't — you'll be using FormRequest, $casts, and Carbon. Only reach for this if you're running Symfony Form standalone (legacy code, shared form definitions, or a Symfony package you're consuming from Laravel) on a Symfony version older than 4.3.
composer require ruwork/polyfill-form-dti
composer.json — as of 0.2.1 the Symfony constraint is relaxed to >=4.1 with no upper-conflict pin, so it will now resolve against modern symfony/form. That does not mean it's needed there; it just means Composer won't stop you.src/ — the actual form type extension(s). There is very little code here; read all of it before debugging. Check whether it implements getExtendedType() or getExtendedTypes().DateTimeType field and ask it to return an immutable object:
$builder->add('publishedAt', DateTimeType::class, [
'input' => 'datetime_immutable',
]);
Without the extension registered, Symfony throws an "invalid input" style error or silently returns a mutable \DateTime. After registration, $form->get('publishedAt')->getData() is a \DateTimeImmutable.If you're on Symfony >= 4.3, stop here — DateTimeImmutableType and input => 'datetime_immutable' are native. This package adds nothing.
Register the extension. In a Symfony app this happens via form.type_extension DI tags. In a Laravel app (or any standalone Form usage), you must wire it manually — there's no autoconfiguration:
// e.g. inside a service provider's register()
$formFactory = $this->app->make(FormFactoryInterface::class);
// Class name comes from src/ — inspect it first.
$formFactory->addTypeExtension(new DateTimeImmutableTypeExtension());
Then any DateTimeType/DateType/TimeType in your form definitions can opt in via 'input' => 'datetime_immutable'.
Pair with the persistence layer consistently. The point of returning an immutable object is usually to store it with Doctrine's datetime_immutable DBAL type or, in Laravel, to bind it to a model cast:
protected $casts = [
'published_at' => 'immutable_datetime',
];
If your field returns DateTimeImmutable but your cast/DBAL type expects mutable, you'll get type errors on save.
Model your domain properties as immutable. The natural workflow is: form type option → \DateTimeImmutable property → immutable DBAL type / cast. Don't mix — a DTO with a \DateTimeImmutable typed property will fatal if the extension isn't active and returns a mutable object.
When bridging into Laravel, register the extension once at boot and keep form-building code isolated in a small adapter layer. Don't scatter new DateTimeTypeExtension() calls around controllers.
Version-gate your registration, not your install. Since 0.2.1 will install on any Symfony >= 4.1, guard registration so the polyfill only runs where it's actually needed:
if (Kernel::VERSION_ID < 40300) {
$formFactory->addTypeExtension(new DateTimeImmutableTypeExtension());
}
DateTimeImmutableType natively; do not add this to a new project.symfony/form, which protected you from yourself. Now it installs cleanly on Symfony 4.1 through 6/7, so a composer require succeeding tells you nothing about whether the extension is compatible with that Symfony's Form internals.src/ still implements getExtendedType() rather than getExtendedTypes(), it will emit deprecations or fail outright on Symfony 4.2+ — exactly the range the widened constraint now lets you install into. Verify src/ before trusting the install.DateTimeType. Symptoms are hard to read (double transforms, timezone applied twice, or "already immutable" type surprises). If you're on 4.3+, remove the package rather than reason about the interaction.FormFactory. Verify by dumping the resolved form config.DateTimeToArrayTransformer etc.). If you get unexpected types, dump $form->get('field')->getData() and getNormData() to see which transformer stage misbehaved. In Symfony apps, bin/console debug:form DateTimeType shows whether datetime_immutable is an accepted input value.model_timezone / view_timezone are respected, but immutable conversions mean you can't mutate in place — call ->setTimezone() and use the returned object, or you'll silently work on a discarded clone.empty_data assumptions. Older form extension code often assumes a mutable object exists to mutate. Empty or invalid submissions can surface as "Call to a member function ... on bool/null" — check empty_data and required behavior when no value is submitted.DateTimeImmutableType; on Laravel, use immutable_datetime casts and FormRequests. 0.2.1 removes an install-time obstacle but not the reason to avoid the package — it remains a last resort for legacy Symfony 3.x/4.0–4.2 applications only.How can I help you explore Laravel packages today?