azjezz/input-hydrator-bundle
Install the Bundle
composer require azjezz/input-hydrator-bundle
(Automatically registers in config/bundles.php if using Symfony Flex.)
Define a DTO Class
Create a simple DTO implementing AzJezz\Input\InputInterface:
// src/Input/CreateUser.php
namespace App\Input;
use AzJezz\Input\InputInterface;
final class CreateUser implements InputInterface
{
public string $name;
public string $email;
}
Use in Controller Inject the DTO directly into a controller method:
// src/Controller/UserController.php
use App\Input\CreateUser;
use Symfony\Component\HttpFoundation\Response;
class UserController
{
public function create(CreateUser $input): Response
{
return new Response("Creating user: {$input->name}");
}
}
Test with Request Send a request with matching input:
curl -X POST http://your-app/user -d "name=John&email=john@example.com"
InputInterface (Contract for DTOs)InputHydrator (Core logic)InputHydratorBundle (Symfony integration)string for text fields).InputInterface for implicit validation.public NestedDto $address; // Automatically hydrates from `address[street]` etc.
RequestStack and ParameterBagInterface.POST/PUT).GET).Content-Type: application/json).AzJezz\Input\Hydrator\HydratorInterface for custom input sources (e.g., headers).public function update(UpdateUser $input, UserRepository $repo): Response
{
$user = $repo->find($input->id);
$user->update($input); // Pass DTO to service layer
}
public function process(CreateUser $input): void
{
// Business logic using $input->name, $input->email
}
public function store(CreateUser $input, ValidatorInterface $validator): Response
{
$errors = $validator->validate($input);
// Handle errors...
}
# config/packages/nelmio_api_doc.yaml
components:
schemas:
CreateUser:
type: object
properties:
name: { type: string }
email: { type: string }
@ApiResource for automatic DTO hydration.Type Mismatches:
BadRequestHttpException if string property receives array.?string for nullable fields.Missing Required Fields:
BadRequestHttpException for undefined properties.public (required) or use ?Type for optional fields.Nested Object Hydration:
address.street for public Address $address).Case Sensitivity:
Name vs name).debug:router and debug:container help inspect resolvers.azjezz.input.hydrator.hydrate events to log hydration steps:
$eventDispatcher->addListener('azjezz.input.hydrator.hydrate', function ($event) {
error_log('Hydrating: ' . print_r($event->getInput(), true));
});
AzJezz\Input\Exception\HydrationException for domain-specific errors.Custom Hydrators:
// src/Hydrator/CustomHydrator.php
use AzJezz\Input\Hydrator\HydratorInterface;
class CustomHydrator implements HydratorInterface
{
public function hydrate(array $data, object $input): void
{
$input->customField = $data['custom_key'] ?? null;
}
}
Register in services:
# config/services.yaml
AzJezz\Input\HydratorBundle\:
arguments:
$hydrators: ['@custom_hydrator']
Override Default Behavior:
# config/packages/azjezz_input_hydrator.yaml
azjezz_input_hydrator:
strict: false
$eventDispatcher->addListener('azjezz.input.hydrator.error', function ($event) {
$event->setResponse(new JsonResponse(['error' => 'Invalid input'], 400));
});
Performance:
autowiring handles this by default).bundles.php addition for older setups.AzJezz\Input\HydratorBundle\DependencyInjection\Compiler\HydratorPass runs early in the container compilation.How can I help you explore Laravel packages today?