hostnet/form-handler-component
Hostnet Form Handler Component offers a reusable way to define, validate, and process forms in PHP applications. It helps organize form logic, handle submissions consistently, and integrate cleanly into existing projects.
The package is a plain Composer library, not a Laravel package — expect no service provider, no facade, and no config file to publish. The repository is unlisted, so treat the installed source as the documentation.
composer require hostnet/form-handler-component
composer show hostnet/form-handler-component
ls vendor/hostnet/form-handler-component/src
composer.json first. It tells you the real integration surface: PHP constraint, whether it pulls in symfony/form, symfony/http-foundation, or PSR-7 messages, and therefore whether you are bridging Symfony's Request or Laravel's Illuminate\Http\Request.src/. In a component like this the entry point is almost always an abstract handler plus a result/value object — that pair is what you bind your own logic to.A typical first pass looks like this (names will differ; match what's in src/):
final class UpdateProfileHandler extends AbstractFormHandler
{
public function __construct(private UserRepository $users) {}
protected function handle(array $data): FormHandlerResult
{
$user = $this->users->update($this->currentUser(), $data);
return FormHandlerResult::success($user);
}
}
Then in the controller:
public function update(Request $request, UpdateProfileHandler $handler)
{
$result = $handler->handle($request->all());
return $result->isValid()
? redirect()->route('profile')->with('status', 'Saved')
: back()->withErrors($result->getErrors())->withInput();
}
If that compiles and the redirect works, you have understood 80% of the package.
One handler per use case. Resist the urge to build a generic CrudHandler. The value of this pattern comes from each handler owning exactly one form lifecycle: bind → validate → mutate → return a result. It keeps handlers testable in isolation and keeps controllers from becoming orchestrators.
Return a result object, not a boolean. The whole point of a form handler is that the outcome carries both success data and validation errors. Map the result onto Laravel's session flash in one place — a small trait or a base controller method — so every form endpoint responds consistently:
protected function respond(FormHandlerResult $result, string $route)
{
if ($result->isValid()) {
return redirect()->route($route)->with('status', 'Saved');
}
return back()->withErrors($result->getErrors())->withInput();
}
Use DTOs if the package supports binding. If there is a data-mapping hook, map raw request arrays into a typed DTO before persistence. This gives you a second validation layer (constructor type checks) and makes handlers easy to call from jobs, commands, and tests without an HTTP request.
Integrate with Form Requests rather than replacing them. Laravel's FormRequest already does authorization and validation well. A common pragmatic split: authorize and validate in the FormRequest, then pass $request->validated() into the handler. You get Laravel's error bag and withInput() behavior for free while still keeping the mutation logic out of the controller.
Bind handlers in a service provider. Since there is no auto-discovery, register handlers (or the interfaces they implement) in AppServiceProvider::register(), or rely on Laravel's constructor injection and let the container resolve them. If the package expects a Symfony-style container or event dispatcher, that is where the adapter work lives.
Test at the handler level. Instantiate the handler directly, feed it an array, assert on the result. No HTTP kernel, no session, no middleware — this is the main reason to adopt the pattern.
Symfony\Component\HttpFoundation\Request, symfony/form, or a EventDispatcherInterface dependency in composer.json. If present, you need an adapter layer: convert Illuminate\Http\Request into the expected type at the controller boundary. Do not leak Symfony request objects into the rest of your Laravel app.getErrors() actually returns — an array keyed by field, a ConstraintViolationList, or a message bag — before wiring it into withErrors(). Convert explicitly.composer.json, read the changelog between upgrades, and keep a thin abstraction so you can swap it out in an afternoon if it stalls.FormRequest already give you a clean 422 response. This package earns its keep on HTML form workflows where you need the success/error bifurcation in one place.How can I help you explore Laravel packages today?