grossum/extended-form-type
Grossum Extended Form Type is a Laravel/PHP package for extending standard form types, making it easier to build reusable, customized form fields with less boilerplate. It fits into existing form workflows and helps keep field definitions clean and maintainable.
This is a small, single-purpose Symfony bundle from 2018 with no stars, no dependents, and no release since April 2018. Treat it as legacy-maintenance-only: the right way to "start" with it is to confirm whether you actually need it, or whether you're inheriting it in an existing composer.json.
1. Confirm compatibility before anything else
composer show grossum/extended-form-type --all
Check the require block for the Symfony constraint. A 2018 bundle will typically pin symfony/form, symfony/framework-bundle, and symfony/dependency-injection to ^2.8|^3.0. On Symfony 4.4+/5+/6+ expect friction: Bundle::boot() signatures, getName() being removed from AbstractType, and container-aware services being deprecated. If you're on Symfony 5+, plan on forking rather than installing.
2. Install and register
composer require grossum/extended-form-type
Symfony 3 style (app/AppKernel.php):
$bundles = [
// ...
new Grossum\ExtendedFormTypeBundle\GrossumExtendedFormTypeBundle(),
];
Symfony 4+ style (config/bundles.php):
return [
Grossum\ExtendedFormTypeBundle\GrossumExtendedFormTypeBundle::class => ['all' => true],
];
3. Where to look first
The repo is tiny — read it in this order:
GrossumExtendedFormTypeBundle.php — confirms the bundle alias and whether it registers compiler passes.DependencyInjection/ — the extension + Configuration.php tell you every config key that exists (likely very few).Form/Type/ (or Form/) — the actual type classes. This is where the value lives.Resources/config/services.yml — shows which services are public, tagged form.type, or tagged form.type_extension.4. First use case
The bundle's premise is that you extend its base type instead of Symfony's AbstractType, and pick up whatever conveniences it layers on (typically shared option handling, default required/placeholder/help behavior, or a helper for translating labels). Start by converting one existing form type to the bundle's base class, render it unchanged, and diff the HTML output and the resolved option array. Do not convert the whole app at once — you need a baseline to compare against.
Pick the least critical form in the app. Swap the parent class only:
use Grossum\ExtendedFormTypeBundle\Form\AbstractType as ExtendedAbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ContactType extends ExtendedAbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email', EmailType::class)
->add('message', TextareaType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Contact::class,
]);
}
}
Render it in Twig exactly as before. Any visible or behavioral change is attributable to the base class — that's your signal.
The usual payoff for a bundle like this is codifying house conventions. If the base type exposes a hook (e.g. a configureDefaults() or a set of extra options), push your app-wide defaults through it rather than repeating 'required' => false in fifty types:
public function configureOptions(OptionsResolver $resolver)
{
parent::configureOptions($resolver);
$resolver->setDefaults([
'attr' => ['class' => 'form-control'],
'translation_domain' => 'forms',
]);
}
Calling parent::configureOptions() first is important — if the bundle sets defaults, doing it in the other order silently overwrites them.
If you only need a cross-cutting concern (a CSRF tweak, an anti-spam honeypot, a "required field" asterisk), use Symfony's native extension mechanism and keep the bundle out of the type hierarchy:
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
class FormControlExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [FormType::class];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('attr', ['class' => 'form-control']);
}
}
This is the safer long-term path: it survives the bundle being dropped, and it doesn't couple every form type in your app to an unmaintained package.
If the bundle's types take constructor arguments (entity repositories, translators), register them explicitly. On Symfony 4+ autowiring will usually pick them up; on older setups add the tag:
services:
app.form.type.contact:
class: AppBundle\Form\ContactType
arguments: ['@doctrine.orm.entity_manager']
tags: ['form.type']
In an inherited codebase where the bundle is load-bearing, don't rip it out in one PR. Instead:
AbstractTypeExtension that reproduces the bundle's behavior.AbstractType.grep -r Grossum returns nothing, remove the dependency.This keeps composer update unblocked while you migrate, which matters because the pinned Symfony constraints will otherwise hold the whole app back.
It is effectively abandoned. Last release 2018-04-13, zero stars, zero dependents. There is no community to catch regressions and no security response. Treat every behavior as "vendor code you own." If it's a genuine dependency, fork it into your VCS and maintain it there — don't rely on Packagist availability.
No dependents means no smoke test. Nobody else has run this against your Symfony version. Budget time for a manual pass over every form that uses it, including edge cases: validation failures, POST re-render with errors, and file uploads (if applicable).
Zero stars also means thin docs. The README is likely 10 lines. The DI configuration and the type classes are the documentation. Run:
bin/console config:dump-reference grossum_extended_form_type
If the alias isn't what you guessed, find it in the bundle class (getName() / getContainerExtension()).
Debug the resolved options, not the type class. The single most useful command when a form behaves unexpectedly:
bin/console debug:form AppBundle\Form\ContactType
It prints the full option tree with resolved defaults and marks which extension contributed each one — that's how you find out exactly what the bundle is injecting.
Watch the option-resolver order. If the bundle's base type sets defaults in configureOptions() and you call $resolver->setDefaults() before parent::configureOptions(), your values win — sometimes you want that, usually you don't. Be explicit in every override.
getName() and form type naming. If the bundle's types implement the old static getName() and you're on Symfony 4.3+, you'll get deprecation notices, and on 5.0 the method is gone. If the bundle calls getName() on your types internally, it will break. Grep for it before upgrading.
Service visibility / services.yml in the bundle. Bundles from this era often declare services without public: false or with a services: block that Symfony 4+ will complain about. If the container fails to compile, the bundle's Resources/config/services.yml is almost always the cause — you can patch it in a fork without changing any PHP.
Templates are a likelier breakage point than PHP. Symfony's form theme blocks (form_row, form_widget) changed subtly across 3.x→4.x→5.x. If the bundle ships Twig overrides, verify they're picked up (debug:twig shows template paths) and that they still override what you expect.
Extension points, in order of preference:
decorates: in services.yml) — good for changing a single behavior without touching vendor code.FormTypeExtension on FormType::class — best when the behavior is generic; it makes the bundle redundant over time.Don't let it pin your Symfony version. The most common real cost of a bundle like this isn't a bug, it's a composer.json conflict that blocks a framework upgrade for the whole project. If you decide to keep it, check its constraints against your target Symfony release before the upgrade sprint, not during it.
How can I help you explore Laravel packages today?