symfony2admingenerator/form-extensions-bundle
Symfony bundle that extends the Symfony2 Admin Generator with extra form types, widgets and field extensions, making it easier to build richer admin forms with nested collections, uploads and other common backend needs.
Check compatibility against the 4.x line, not the README. The project is now shipping v4.1.2, so the ~1.0 constraint you may have seen in older docs/blog posts is stale. Start with composer.json in the tag you intend to install: read the require block for the Symfony range, PHP floor, and Twig version. The v4.1.2 patch ("Fix reference to deprecated Extension class") is a signal in itself — it means the bundle's DI extension now extends the non-deprecated base, which only matters on Symfony 5.1+. Treat the 4.x line as the "modern" branch and the 1.x/2.x tags as the Admingenerator-era reference implementation.
A useful rule of thumb: if you are on Symfony 2.x/3.x, you want the old tags and the old AppKernel/Assetic instructions. If you are on Symfony 5.4/6.x, the 4.x line is your entry point — but verify the require block before assuming it is supported, because the changelog is one line and tells you nothing about the supported matrix.
Install:
composer require symfony2admingenerator/form-extensions-bundle:^4.1
Pin the minor. Do not use dev-master — with a project this small, master can drift to a commit expecting a Symfony version you do not have, and there will not be a release note warning you.
Register the bundle in config/bundles.php on Symfony 4+ (Symfony2admingenerator\FormExtensionsBundle\FormExtensionsBundle::class => ['all' => true]). Confirm the class name against the file in the repo root; the namespace casing (Symfony2admingenerator) is legacy and easy to get wrong.
Register the form types as services. On Symfony 5+, string aliases are gone — every type you use must either be a service tagged form.type or be referenced by FQCN. Check Resources/config/*.xml for whether the bundle tags its own types (it does, historically) and whether the tags survive into your installed version. If a widget renders but the field throws "could not load type", this is the cause.
Where to look first, in this order:
Form/Type/ — the authoritative list of what you get: double list, upload/image, date/time pickers, WYSIWYG (CKEditor/TinyMCE/MarkItUp), elFinder, file-upload collections, AJAX autocomplete.Resources/config/*.xml — the exact aliases (double_list, upload, image, ckeditor, date_picker, datetime_picker, fileupload, autocomplete, elFinder, …) and the options each declares. Do not guess option names from the README; configureOptions() is the source of truth.Resources/views/Form/ — the layouts. This is where you will spend most of your time overriding.First use case — replace a dull multi-select with the double list:
$builder->add('roles', DoubleListType::class, [
'choices' => $roleChoices,
'multiple' => true,
'attr' => ['class' => 'col-md-6'],
]);
Then immediately verify the value round-trips: the widget posts a hidden input with the ordered selection, and re-rendering on validation failure is the first thing that breaks.
Use it as a "widget catalogue", not a black box. The value is the Twig + JS glue for widgets Symfony doesn't ship natively. Adopt it field-by-field; do not retrofit your whole app to its conventions.
Upgrades within 4.x are cheap. v4.1.2 is a single-line DI fix with no API surface change — no form type options, templates, or aliases moved. Treat patch releases here as low-risk bumps: run your form smoke tests (render, submit, validation-failure re-render) and you are done. The real upgrade risk in this package lives in minor/major bumps and in Symfony upgrades, not in patches.
Pattern: double list for roles / permissions / tags.
Best when order matters or the candidate set is too large for <select multiple>. Pair with a PRE_SUBMIT/POST_SUBMIT listener if you must normalise the array (e.g. strings → entity IDs). For a Doctrine collection, add a ModelTransformer — the type only transports strings.
Pattern: upload / image with lifecycle handling. The upload types render input plus preview/delete UI; they persist nothing.
'mapped' => false).prePersist/preUpdate Doctrine listener (or the controller), move the file out of upload_dir/upload_tmp_dir.$builder->add('attachment', FileUploadType::class, [
'mapped' => false,
'upload_dir' => 'uploads/documents', // verify key against the type
'max_size' => '8M',
]);
Pattern: rich text (CKEditor / TinyMCE / MarkItUp). Configure the toolbar/profile once via bundle config, then add the type per field. Sanitise on render — these types store raw HTML; run HTMLPurifier (or equivalent) in your output layer, not in the form.
Pattern: AJAX autocomplete for large entity sets.
The autocomplete family expects a JSON endpoint returning objects with id and a display label.
path() in the widget).LIKE %q%), return [{id: 1, text: '...'}].ModelTransformer (Doctrine's EntityType transformer works) so the ID string becomes an entity.Pattern: collection with add/remove.
Combine Symfony's native collection type with the bundle's JS (allow_add, allow_delete, prototype). The templates render the buttons; you supply the prototype and data-prototype. If the bundle JS fights yours, override the template and keep only the markup you need.
Pattern: Admingenerator integration.
If you use symfony2admingenerator/generator-bundle, these types are exposed from generator YAML:
fields:
roles:
formType: double_list
avatar:
formType: image
formOptions:
upload_dir: uploads/avatars
That is the intended consumer — if that is your stack, friction is low. If not, you are using it out of context.
Pattern: standardise with a base type.
Wrap recurring fields (rich text, image + preview, autocomplete) in your own type classes that hard-code sane defaults (required => false, size limits, CSS classes). When the bundle is eventually removed, you change three files instead of forty — and this also insulates you from alias/service-tag churn across Symfony upgrades.
Template overrides. Override individual widgets rather than whole themes:
# config/packages/twig.yaml
twig:
form_themes:
- 'Form/overrides.html.twig' # extends the bundle's widget blocks
Or copy a block verbatim from Resources/views/Form/ and edit it. Cheapest way to fix Bootstrap 2 → 5 markup skew and to drop jQuery UI for your own datepicker.
double_list, but Symfony 5+/6 resolves types by FQCN and by service tag. Write new code with FQCNs (Symfony2admingenerator\FormExtensionsBundle\Form\Type\DoubleListType::class); fall back to the alias only if you have explicitly registered it yourself. This is the single most common "it worked in the tutorial" failure.use statement into your own code — in which case swap the deprecated Extension import for the current one. Nothing else in the patch touches runtime behaviour.%kernel.project_dir% parameter, or a leading / can silently produce a path outside your writable tree. Assert is_writable() in a smoke test.Slugger/uniqid) in your persistence listener, and validate the extension against an allow-list via File::guessExtension().onFlush/postRemove handler, or compare against $em->getUnitOfWork()->getOriginalEntityData() in preUpdate.date() tokens, not ICU/Intl patterns. 'format' => 'dd/MM/yyyy' will not do what you expect; the JS side may use a different format string than the PHP side. Mismatches show up as the picker silently rejecting valid input.twig.form.resources order matters. An unstyled widget is almost always an earlier-registered theme claiming the block. Inspect with {{ dump(form.vars) }} and reorder form_themes (last wins for block inheritance, first wins for resource search — test empirically).form_extensions: by convention), often consumed by a Twig extension injecting <script>/<link> tags. If assets aren't injected, the extension isn't registered or the node name differs — read DependencyInjection/Configuration.php, not the README.services.yaml, (d) a compiler pass to re-tag. There is no modern autoconfiguration.twig.form_themes) → JS console for missing jQuery UI/plugins → type registered (debug:container grep for the FQCN or alias) → option names against configureOptions() → network tab for AJAX 302s.LICENSE file before shipping commercially; do not assume MIT because the ecosystem usually is.symfony/ux (Stimulus controllers for tom-select, file previews), VichUploaderBundle for uploads, FOSCKEditorBundle for rich text, and EasyAdmin/Sonata collection fields where they apply. Use this bundle when you are already on the Admingenerator stack and want the widgets for free; otherwise cherry-pick the type classes and templates you need into App\Form\Type / templates/form and drop the dependency.How can I help you explore Laravel packages today?