solidworx/form-handler-bundle
SolidWorx FormHandlerBundle integrates the FormHandler library into Symfony, letting you process and validate submitted form data through reusable handler classes. It simplifies mapping request data to objects and keeps controllers clean.
First, a framing note: despite the "Laravel developer" lens, solidworx/form-handler-bundle is a Symfony bundle, not a Laravel package. It won't drop into a Laravel app via composer require and a service provider. If you're working in Laravel day-to-day, the practical use case is narrow: you're maintaining or migrating a Symfony service, or you're evaluating form-handling patterns to port concepts into Laravel (where you'd normally reach for FormRequest + DTOs or Livewire instead).
Assuming you're in a Symfony codebase:
Install into a Symfony app:
composer require solidworx/form-handler-bundle
Check composer.json for the supported Symfony versions first — this package has 7 stars and 0 dependents, so version constraints are likely stale relative to Symfony 6/7. If Composer refuses the install, note the required symfony/framework-bundle range before going further.
Register the bundle. Symfony Flex may or may not have an auto-config recipe. If config/bundles.php doesn't gain an entry, add it manually:
SolidWorx\FormHandlerBundle\FormHandlerBundle::class => ['all' => true],
Look here first (in this order):
README.md — the primary documentation source for a package this small.src/ for the base FormHandler class/interface and any PHP attributes/annotations that bind a handler to a form type.DependencyInjection/ for the service definitions the bundle registers (tells you what's auto-wired).tests/ — often the most accurate spec for a low-traffic package.First use case — one form, one handler. The core idea is: instead of stuffing form creation, submission, validation, and persistence into a controller action, you define a handler class that owns that flow and inject it into the controller. Start with a single "contact form" equivalent: a form type, a handler that calls handleRequest, checks isValid, then redirects on success. That isolates the pattern before you spread it across the app.
If you want a concrete first milestone: take one existing controller action with a $form->handleRequest($request) block, extract it into a handler, and confirm the redirect/success path is unchanged.
One handler per form, registered as a service. The value of the package only materializes if handlers are services — that's what gives you constructor injection of the entity manager, mailer, logger, etc. Don't instantiate handlers with new; let the container wire them and autowire the handler into your controller signature.
Thin controllers, fat-but-focused handlers. The day-to-day pattern is a controller action that does roughly: return $this->getHandler(MyFormHandler::class)->handle($request) or injects the handler and delegating the request/response cycle to it. Your controller should shrink to routing and access control.
Attribute/annotation-based binding. The bundle is built around declarative binding of a handler to a form type (this is the "easy form handling" pitch). Use the attribute to declare which form type a handler owns rather than duplicating configuration in YAML. Keep the mapping co-located with the handler class so refactors stay in one file.
Success/failure branching lives in the handler. Model the flow as: build form → handleRequest → isValid → on success perform side effects (persist, dispatch event, redirect); on failure return the rendered form view. This keeps validation and error rendering consistent across every form in the app.
Shared base handler for cross-cutting concerns. If several handlers need CSRF toggling, flash messaging, or a logging call on submit, put that in a small app-level abstract base extending the package's handler rather than repeating it per form.
Integrate with the event dispatcher for side effects. Rather than doing slow work (email, queueing) inline after isValid, dispatch an event/listener from the handler. Handlers stay testable and the success path stays fast.
Testing pattern. Because handlers are services with constructor dependencies, they're straightforward to unit test with mocked collaborators; keep the HTTP-level assertions in functional tests. This split is the main reason to adopt the pattern at all.
This is a Symfony bundle — do not attempt a Laravel install. There is no service provider, no config publish tag, no facade. In Laravel, the equivalent value comes from FormRequest validation, single-action controllers, and DTO/job pipelines. Treat this package as reference material, not a dependency, unless you're genuinely in Symfony.
Version/bundle registration friction is the most likely first failure. With 0 dependents and low stars, expect no Flex recipe or a stale one. Symptoms: Class "SolidWorx\...\FormHandlerBundle" not found (autoload/namespace mismatch — note the casing difference between the repo name and the SolidWorx namespace) or a config/bundles.php entry that was never added. Fix by checking vendor/solidworx/.../composer.json for the real namespace and PSR-4 mapping.
Autowiring gaps. Small bundles often register services under explicit definitions rather than _defaults: autowire: true. If a handler isn't injectable, run:
bin/console debug:container --show-private | grep -i handler
and if nothing appears, define the service yourself in config/services.yaml with autowire: true and a resource: glob over your handler directory.
Debugging form submission flows. When a form "does nothing":
bin/console debug:router
bin/console debug:container FormHandler
Then confirm handleRequest($request) is actually receiving the POSTed request (a common mistake is calling a handler that internally re-reads the request instead of the one passed in). Add a temporary dump($form->getErrors(true)) after the validity check.
Config quirks. If the bundle exposes configuration under a solidworx_form_handler: key, verify it's loaded by inspecting the container parameter — a wrong root key silently does nothing in Symfony. Check DependencyInjection/Configuration.php for the exact tree rather than guessing from the README.
Extension points to know. The useful seams are: the base handler class (override lifecycle hooks), the form-type resolution (custom resolver for dynamic form types), and any submit/success events. Prefer subclassing the handler over editing vendor code — this package is small enough that you may eventually vendor-patch it, but keep that as a last resort since updates are rare and you'd be maintaining a fork.
Risk assessment. With a 7-star, 0-dependent package and a mid-2025 last release, treat it as low-bus-factor. Before standardizing on it, confirm it's covered by your Symfony version in CI and that you're comfortable owning the handler base class if the maintainer goes quiet. For new projects, weigh it against plain Symfony form handling in a controller plus a service — the bundle saves configuration, not architecture, and that configuration is cheap to replicate.
How can I help you explore Laravel packages today?