netvlies/form-bundle
Netvlies FormBundle extends Symfony's form component with reusable form types, extensions and helpers, making it easier to build, customize and render complex forms in PHP applications.
This bundle targets Symfony 2.x with Sonata Admin. Before writing any code, confirm it will actually install in your project — on a modern Laravel app (or Symfony 5/6/7), there is no adapter path; treat it as a reference implementation for a forms module. Use it only in a legacy Symfony 2 maintenance app, or plan to fork and own it.
Confirm the constraint match. Read the bundle's composer.json require block first. It pins Sonata Admin, Doctrine, and SwiftMailer versions — those dictate whether it can coexist with the rest of your stack. If Composer cannot resolve, stop here; do not "fix" it by loosening constraints.
Install with Composer against the Symfony 2 constraint your app already uses:
composer require netvlies/form-bundle
Register the bundle in app/AppKernel.php, along with its dependencies (SonataAdminBundle, SonataDoctrineORMAdminBundle, DoctrineBundle, SwiftmailerBundle).
Create the schema from the shipped Doctrine entities (forms, fields, stored results). Generate a migration — never use schema:update in production:
php app/console doctrine:migrations:diff
php app/console doctrine:migrations:migrate
Configure in app/config/config.yml: mailer from/to addresses, storage, export settings. Verify the exact config keys against the README for the version you installed — they are not stable across tags.
Where to look first:
README.md — config keys and the intended workflow.Admin/ — FormAdmin and field admin classes show how a form is modeled and edited.Entity/ — understand the form → field → result relationships before writing any query against submissions.Symfony\Component\Form\Form at runtime.First use case: create a form in Sonata Admin, add a few fields (text, email, textarea, select), set a recipient, embed the rendered form in a Twig template, submit it, and verify both outcomes — the result row is persisted and the notification email is delivered. That single loop exercises every moving part in the bundle.
Admin-driven form definition. Forms are Doctrine entities managed through Sonata Admin. Each field carries a type, label, required flag, and position. The admin is the source of truth: developers do not hardcode front-end fields — the bundle compiles the stored definitions into a Symfony Form at runtime. When you need a new field type, you add it to the field-type registry (and typically a Twig block for rendering) rather than touching each consumer.
Runtime rendering. The typical integration is a controller route that loads the form entity, builds the Symfony Form, handles the request, persists the result, and sends mail. Embed it from a page template:
{{ render(controller('NetvliesFormBundle:Form:show', { id: form.id })) }}
Use render(controller(...)) (a sub-request) rather than render('...:show.html.twig') directly, because the controller is what attaches the CSRF token, form state, and validation errors.
Storage and reporting. Submissions are persisted as Doctrine entities linked back to the form and its field definitions. Query through the entity repositories, not raw SQL — the relationship graph (form ↔ field ↔ value) is the contract. For export, drive it off the same repository query and stream the file; do not hydrate everything into memory if volumes are non-trivial.
Notification on submit. Mail is sent as part of the submit handler. Treat email as a notification, not the source of truth: the submission row is durable, the mail is best-effort. If you need retries or delivery visibility, wrap the send in your own service, log outcomes, and consider a queue (in a legacy Symfony 2 app, that typically means a lightweight messenger or a cron-driven outbox — SwiftMailer's spool can work if you configure it).
Reusable form types. Prefer composing shared Symfony form types (address block, contact block, choice sets) and referencing them from stored field definitions, rather than repeating field configs across forms. This is where the bundle's "clean reusable layer" value shows up in day-to-day work.
Extending workflows. The integration points, in order of usefulness:
FormAdmin for per-form behavior;field.type into a Symfony field type, which is the natural seam for adding new types without touching core code.Framework mismatch is total. On Laravel there is no integration path — no service provider, no Eloquent mapping, no Blade equivalent of its Twig wiring. If you landed here from a Laravel roadmap, read it as a spec (form-definition schema, submission table, templated notification, CSV export) and rebuild on Laravel with Filament/Nova, Mail + queues, and maatwebsite/excel. Do not attempt an adapter.
EOL stack. Symfony 2.8 reached end-of-life in 2018, and Sonata Admin + Doctrine + SwiftMailer from that era are similarly abandoned. Direct use is defensible only inside an already-legacy Symfony 2 service with a bounded lifespan and a named owner for patches. Assume no upstream fixes are coming.
Config keys drift between versions. The README is version-specific and thin. Before wiring config, read the bundle's DependencyInjection/Configuration.php — that file, not the README, is the authoritative list of options (and their defaults).
Schema drift. Because forms and fields are rows, changing a field definition affects future renders but not historical submissions. Decide up front whether to snapshot the field definition on each submission (recommended) — otherwise editing a form silently changes how past results are interpreted in exports.
Mail failures are silent. SwiftMailer send can fail (transport down, bad recipient) without surfacing to the user. Log sends and failures explicitly, and test with a failover/spool transport before going live. Public forms also attract spam — the bundle's flat model has no built-in captcha/rate limiting; add a Symfony form extension or a middleware-like guard yourself.
Export is memory- and timeout-sensitive. Batch/stream exports and cap the result set, or a large form will exhaust memory. Test with realistic volumes, not three rows.
Sonata Admin is the security surface. Admin-defined forms mean non-developers can create public-facing input surfaces. Restrict who can publish forms, disable raw HTML/file-upload field types unless explicitly needed, and review the admin ACLs — a misconfigured admin is an injection and spam vector, not just a UX issue.
CSRF and validation. Embed via the controller sub-request so CSRF and validation errors render correctly. If you bypass the controller (e.g., constructing the form in your own service), you take on token generation, error display, and redirect-after-post yourself.
Testing. Assume minimal test coverage in the bundle. Add your own functional tests around the submit path (persist + mail) and the export endpoint; those are the two places a regression is most expensive and least obvious.
Extension points worth knowing. The rendering Twig blocks, the field-type-to-form-type mapping service, and a subclass of FormAdmin are the three seams most day-to-day changes flow through. Keep customizations in your app's overrides, not edits to the vendored package, so upgrades (or a fork) stay tractable.
How can I help you explore Laravel packages today?