Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Request Param Bundle Laravel Package

baptiste-contreras/symfony-request-param-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle

    composer require baptiste-contreras/symfony-request-param-bundle
    

    Register the bundle in config/bundles.php:

    return [
        // ...
        BaptisteContreras\SymfonyRequestParamBundle\SymfonyRequestParamBundle::class => ['all' => true],
    ];
    
  2. Define a DTO Class Create a DTO class (e.g., RegisterRequest) with validation rules (e.g., using Symfony Validator):

    use Symfony\Component\Validator\Constraints as Assert;
    
    class RegisterRequest
    {
        #[Assert\NotBlank]
        public string $name;
    
        #[Assert\Email]
        public string $email;
    }
    
  3. Annotate Controller Action Use #[DtoRequestParam] on the DTO parameter in your controller:

    use BaptisteContreras\SymfonyRequestParamBundle\Attribute\DtoRequestParam;
    
    #[Route('/register', name: 'register', methods: ['POST'])]
    public function register(#[DtoRequestParam] RegisterRequest $request): Response
    {
        // $request is now populated and validated
    }
    
  4. Enable Auto-Provision (Optional) Add #[AutoProvideRequestDto] to the controller to auto-provision DTOs for all actions:

    #[AutoProvideRequestDto]
    class RegisterController extends AbstractController
    {
        // ...
    }
    

First Use Case: JSON Request Handling

Replace manual JSON decoding and validation with a single DTO parameter:

#[Route('/user', name: 'user_create', methods: ['POST'])]
public function createUser(#[DtoRequestParam] UserDto $userDto): Response
{
    // $userDto is ready for use (e.g., $userDto->name, $userDto->email)
}

Request Body:

{
    "name": "John Doe",
    "email": "john@example.com"
}

Implementation Patterns

1. DTO-Centric Workflow

  • Replace Manual Parsing: Eliminate repetitive code for JSON/XML/form data parsing and validation.

    // Before:
    $data = json_decode($request->getContent(), true);
    $validator = new Validator();
    $errors = $validator->validate($data, $constraints);
    
    // After:
    public function submit(#[DtoRequestParam] SubmitDto $dto): Response
    {
        // $dto is validated and populated
    }
    
  • Reuse DTOs Across Controllers:

    // UserController.php
    public function update(#[DtoRequestParam] UserDto $dto): Response { ... }
    
    // AdminController.php
    public function bulkUpdate(#[DtoRequestParam] array $dtos): Response { ... }
    

2. Source Type Flexibility

Leverage sourceType to handle different input formats (default: SourceType::JSON):

// Query Parameters
#[DtoRequestParam(sourceType: SourceType::QUERY)]
public function search(SearchDto $dto): Response { ... }

// Form Data
#[DtoRequestParam(sourceType: SourceType::FORM)]
public function createFromForm(FormDto $dto): Response { ... }

Note: Ensure a DtoProviderDriverInterface exists for custom sourceType (e.g., SourceType::XML).


3. Validation Integration

  • Group Validation:

    #[DtoRequestParam(validateDto: true, validationGroups: ['create'])]
    public function create(CreateDto $dto): Response { ... }
    

    Define groups in your DTO:

    class CreateDto {
        #[Assert\NotBlank(groups: ['create'])]
        public string $field;
    }
    
  • Disable Validation:

    #[DtoRequestParam(validateDto: false)]
    public function debug(DebugDto $dto): Response { ... }
    

4. Error Handling

  • Silent Failure:

    #[DtoRequestParam(throwDeserializationException: false)]
    public function safeUpdate(SafeDto $dto): Response { ... }
    

    Returns null on failure (check with null !== $dto).

  • Custom Error Responses: Use Symfony’s ExceptionListener to handle DeserializationException globally.


5. Testing

Mock DTOs in tests:

public function testRegister(): void
{
    $dto = new RegisterRequest();
    $dto->name = 'Test';
    $dto->email = 'test@example.com';

    $this->container->get('request_stack')->push(
        Request::create('/register', 'POST', [], [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($dto))
    );

    $this->client->request('POST', '/register');
    // Assert response...
}

Gotchas and Tips

Pitfalls

  1. Missing Drivers:

    • Error: NoDtoProviderDriverFoundException for unsupported sourceType.
    • Fix: Implement a custom DtoProviderDriverInterface or stick to default types (JSON, QUERY, FORM).
  2. Circular References:

    • DTOs with circular references (e.g., UserProfile) may fail silently or throw exceptions.
    • Workaround: Use #[Assert\Valid] sparingly or flatten nested objects.
  3. Case Sensitivity:

    • JSON keys must match DTO property names exactly (case-sensitive).
    • Tip: Use #[Assert\Property] or a custom DtoProviderDriver for case-insensitive mapping.
  4. Validation Overhead:

    • Disabling validation (validateDto: false) bypasses Symfony’s validator.
    • Tip: Use #[Assert\Callback] for complex validation logic.

Debugging

  1. Check Deserialization: Enable debug mode to see raw request data:

    #[DtoRequestParam(throwDeserializationException: true)]
    public function debug(DebugDto $dto): Response { ... }
    

    Catch DeserializationException to inspect failed data:

    try {
        $this->register($dto);
    } catch (DeserializationException $e) {
        dd($e->getFailedData()); // Raw input that failed
    }
    
  2. Log Validation Errors: Use Symfony’s Validator component to log errors:

    $errors = $validator->validate($dto);
    foreach ($errors as $error) {
        $this->logger->error($error->getMessage());
    }
    

Configuration Quirks

  1. Bundle Auto-Registration:

    • If using Symfony Flex, the bundle auto-registers. For manual setups, ensure:
      // config/packages/symfony_request_param.yaml
      symfony_request_param:
        auto_provide: true  # Optional: enable globally
      
  2. Priority in Dependency Injection:

    • DTOs are resolved before other parameters. Avoid side effects in constructors:
      // Bad: Constructor modifies static state
      class BadDto {
          public function __construct() { StaticService::setContext('bad'); }
      }
      
      // Good: Lazy initialization
      class GoodDto {
          private ?Context $context = null;
      }
      

Extension Points

  1. Custom Source Types: Implement DtoProviderDriverInterface for new formats (e.g., SourceType::GRAPHQL):

    class GraphQLDriver implements DtoProviderDriverInterface
    {
        public function provideDtoFromSource(
            string $sourceType,
            mixed $data,
            string $dtoClass,
            array $options
        ): object {
            // Parse GraphQL input into $dtoClass
        }
    }
    

    Register the driver in the bundle’s services:

    # config/services.yaml
    BaptisteContreras\SymfonyRequestParamBundle\DtoProviderDriverInterface:
        tags: [symfony_request_param.driver]
    
  2. Override Default Behavior: Extend DtoRequestParam attributes or use middleware to pre-process requests:

    // src/EventListener/RequestListener.php
    public function onKernelRequest(RequestEvent $event): void
    {
        $request = $event->getRequest();
        if ($request->isMethod('POST') && $request->getPathInfo() === '/custom') {
            $request->attributes->set('custom_dto', new CustomDto());
        }
    }
    
  3. Integrate with API Platform: Use #[DtoRequestParam] alongside #[ApiResource] for hybrid validation:

    #[ApiResource]
    #[DtoRequestParam(sourceType: SourceType::JSON)]
    class PostDto extends AbstractDto
    {
        #[Assert\Length(min: 10)]
        public string $content;
    }
    

Performance Tips

  1. Cache DTO Metadata: Symfony’s validator caches metadata by default. For large DTOs, ensure:
    # config/packages/validator.yaml
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware