sfmok/request-input-bundle
A PHP package that bundles convenient request input helpers for retrieving, validating, and transforming incoming request data through clean, typed accessors. It reduces controller boilerplate and keeps input handling consistent, predictable, and safe across your application.
sfmok/request-input-bundle is a Symfony bundle, not a Laravel package — it ships no service provider, no auto-discovery, and nothing to php artisan vendor:publish. What it gives you is an argument resolver that turns raw request data into typed input objects, so a controller action can receive a hydrated DTO instead of reaching into $request->query/$request->request by hand. Treat it as a focused alternative to hand-rolled hydration plus a validator call.
First steps, in order:
^2.0 will not resolve in a standard Laravel app. Install ^1.2 unless you are on a Symfony 8 / PHP 8.4 runtime.A good first use case is a read-only, filter-heavy endpoint (search, index, reporting) where you currently do fifteen $request->input(...) calls with inline casts. Define one input object with typed public properties, bind it, and delete the casting block. Save write endpoints for later — they need error mapping (see Gotchas) before they're worth it.
One input class per action, not per resource. These objects are request-shaped, not domain-shaped. SearchUsersInput with ?string $q, int $page, array $roles is fine; reusing it across create/update/index is how you end up with a class where half the properties are conditionally required.
Don't import it into controllers directly if you can avoid it. In a Laravel codebase the cleanest wiring is a thin adapter layer: a base FormRequest-like class or a middleware that (a) hydrates the input object, (b) runs Validator::make($input->toArray(), $rules), (c) throws ValidationException on failure, (d) rebinds the input into the container for the controller to type-hint. That keeps the vendor type out of your action signatures and makes swapping the bundle out a one-file change.
Keep the bundle on the edge of the app. Flow: request → input object → command/action → domain. Pass $input->toArray() or explicit constructor args into the action; never let an input object reach a repository or an Eloquent model.
Pair with the Symfony Validator or Laravel's validator, not both. If you already have Laravel validation rules in FormRequest classes, mirror them as a rules() method on the input object and let the adapter call one validator. Two validation systems on the same payload is a debugging tax with no upside.
Nested input objects for structured payloads. A CreateOrderInput holding an AddressInput and a Collection<LineItemInput> beats flattening into a dozen top-level fields — the nesting is where typed hydration pays for itself, and it's also where the bundle is most likely to need a custom denormalizer (see Gotchas).
Test the hydration, not just the endpoint. A small unit test that constructs a Request with a given query/body and asserts the resulting input object is cheap, fast, and catches almost every regression you'll actually hit.
^1.2 until Laravel's Symfony component floor moves. Do not let Dependabot open the PR.^2.0 will fail install before it fails tests — check platform.php in composer.json.composer require is the easy half. You must register the resolver yourself. If you only ever get "unresolvable dependency" / "Target class does not exist" errors on the input class, the resolver isn't wired — that's the wiring, not your DTO."" for cleared fields; a non-nullable ?int $page will blow up or coerce to 0 depending on the resolver path. Normalize an empty string to null in the input object's constructor or accept ?string and cast downstream.?tag=a&tag=b) arrive as arrays only sometimes. Test this explicitly if you accept list filters; a single ?tag=a may hydrate as a scalar and fail on an array type.errors bag or a redirect-back-with-input for web routes. Wrap the hydration call and rethrow as ValidationException::withMessages(...) unless you're building a JSON-only API.UploadedFile handling lives in FileBag; keep those endpoints on a normal FormRequest.null or defaults, dd($request->getContent(), $request->query->all()) before the resolver runs. The failure mode is usually "the key isn't where the resolver looks" (query vs body vs attributes), not a type problem.CarbonImmutable — rather than teaching each input class to parse them in a constructor. That is the one place this bundle is genuinely nicer than a FormRequest.How can I help you explore Laravel packages today?