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

Request Validation Bundle Laravel Package

choz/request-validation-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Installation: Run composer require choz/request-validation-bundle in your Symfony project. Enable the bundle in config/bundles.php:

    Choz\RequestValidationBundle\ChozRequestValidationBundle::class => ['all' => true],
    
  2. First Use Case: Create a request validation class (e.g., TagCreateRequest) extending BaseRequest and define validation rules in the rules() method:

    use Choz\RequestValidationBundle\Request\BaseRequest;
    use Symfony\Component\Validator\Constraints\Collection;
    use Symfony\Component\Validator\Constraints\Required;
    use Symfony\Component\Validator\Constraints\Type;
    
    class TagCreateRequest extends BaseRequest {
        protected function rules(): array {
            return [
                new Collection([
                    'id' => [new Required(), new Type('int')],
                    'name' => [new Required(), new Type('string')],
                ]),
            ];
        }
    }
    
  3. Inject and Use: Inject the request class into your controller and access validated data via getter methods:

    #[Route('/tags', methods: ['POST'])]
    public function create(TagCreateRequest $request): JsonResponse {
        $id = $request->getInteger('id');
        $name = $request->getString('name');
        // Use validated data
    }
    
  4. Error Handling: The bundle automatically returns a structured JSON error response (HTTP 400) for invalid requests:

    {
        "message": "The given data failed to pass validation.",
        "errors": {
            "id": ["This field is missing."],
            "name": ["This value should be of type string."]
        }
    }
    

Implementation Patterns

1. Request Validation Workflow

  • Declarative Rules: Define validation logic in a dedicated BaseRequest subclass, keeping controllers clean.
  • Reusable Validation: Share request classes across controllers (e.g., UserUpdateRequest for both PUT /users/{id} and PATCH /users/{id}).
  • Type-Safe Getters: Use generated getters (e.g., getInteger(), getString()) to enforce type safety at the request level:
    public function getId(): int {
        return $this->getInteger('id');
    }
    

2. Integration with Symfony Ecosystem

  • Dependency Injection: Request classes are automatically validated via Symfony’s parameter container.
  • Custom Constraints: Extend Symfony’s validation constraints (e.g., @Assert\Callback) for complex rules:
    use Symfony\Component\Validator\Constraints\Callback;
    
    protected function rules(): array {
        return [
            new Collection([
                'email' => [
                    new Required(),
                    new Type('string'),
                    new Callback([$this, 'validateCustomEmail']),
                ],
            ]),
        ];
    }
    
    public function validateCustomEmail($value, Constraint $constraint) {
        if (!str_contains($value, 'example.com')) {
            return 'Email must be from example.com';
        }
    }
    

3. API-Specific Patterns

  • JSON Requests: Pair with symfony-bundles/json-request-bundle for seamless JSON payload validation.
  • Nested Validation: Use Collection constraints for nested objects/arrays:
    new Collection([
        'user' => [
            new Collection([
                'name' => [new Required(), new Type('string')],
                'roles' => [new Type('array')],
            ]),
        ],
    ]);
    

4. Testing Strategies

  • Unit Test Requests: Mock BaseRequest and assert validation errors:
    public function testInvalidRequest() {
        $request = new TagCreateRequest();
        $request->setData(['id' => 'invalid', 'name' => 123]);
    
        $this->expectException(ValidationFailedException::class);
        $request->validate();
    }
    
  • Controller Testing: Use HttpClient to send malformed requests and verify error responses:
    $response = $client->request('POST', '/tags', [
        'json' => ['id' => 'not_an_int'],
    ]);
    $this->assertEquals(400, $response->getStatusCode());
    

5. Performance Considerations

  • Lazy Validation: Validation occurs only when the request is injected into a controller (Symfony’s autowiring handles this).
  • Constraint Groups: Optimize validation by grouping constraints (e.g., new GroupSequence(['create', 'update'])).

Gotchas and Tips

Pitfalls

  1. Missing JsonRequestBundle for JSON APIs:

    • Without symfony-bundles/json-request-bundle, JSON payloads may not be parsed correctly.
    • Fix: Install the bundle and ensure json_request is enabled in config/packages/framework.yaml:
      framework:
          json_request:
              enabled: true
      
  2. Overriding Default Error Responses:

    • The bundle uses Symfony’s ValidationFailedException by default. Customizing error formats requires overriding the event listener (see below).
    • Tip: Use response_code in config/packages/choz_request_validation.yaml to change the HTTP status (e.g., 422 for HTTP_UNPROCESSABLE_ENTITY):
      choz_request_validation:
          response_code: !php/const Symfony\Component\HttpFoundation\Response::HTTP_UNPROCESSABLE_ENTITY
      
  3. Type Safety in Getters:

    • Getters like getInteger() throw exceptions if the field is missing or invalid. Avoid silent failures by wrapping calls:
      try {
          $id = $request->getInteger('id');
      } catch (ValidationFailedException $e) {
          // Handle missing/invalid field
      }
      
  4. Constraint Order Matters:

    • Symfony validates constraints in the order they’re defined. Place Required before Type to fail fast:
      new Collection([
          'id' => [new Required(), new Type('int')], // Correct
          // vs.
          'id' => [new Type('int'), new Required()], // May validate type on missing field
      ]);
      
  5. Circular Dependencies:

    • Avoid circular references in nested Collection constraints (e.g., user.address.city referencing user). Use Callback constraints for dynamic validation.

Debugging Tips

  1. Enable Symfony Debug Mode:

    • Symfony’s debug toolbar shows validation errors in detail during development.
  2. Log Validation Errors:

    • Extend the event listener to log errors for debugging:
      // src/EventListener/CustomRequestValidationEventListener.php
      public function onKernelException(GetResponseForExceptionEvent $event) {
          $exception = $event->getThrowable();
          if ($exception instanceof ValidationFailedException) {
              error_log('Validation errors: ' . print_r($exception->getErrors(), true));
          }
      }
      
  3. Validate Raw Data:

    • Use Symfony’s ValidatorInterface directly to debug constraints:
      $validator = $this->container->get('validator');
      $errors = $validator->validate($data, $constraints);
      

Extension Points

  1. Custom Error Formatters:

    • Override the default JSON response by creating a custom event listener:
      # config/services.yaml
      services:
          App\EventListener\CustomValidationListener:
              tags:
                  - { name: kernel.event_listener, event: kernel.exception, method: onValidationException }
      
      public function onValidationException(GetResponseForExceptionEvent $event) {
          $exception = $event->getThrowable();
          if ($exception instanceof ValidationFailedException) {
              $event->setResponse(new JsonResponse([
                  'errors' => $this->formatErrors($exception->getErrors()),
              ], 422));
          }
      }
      
  2. Dynamic Constraints:

    • Use Callback constraints to add runtime logic:
      new Callback(function ($value) {
          return $value === 'admin' ? 'Admin role is restricted' : null;
      })
      
  3. Validation Groups:

    • Split constraints into groups (e.g., create, update) and validate selectively:
      new GroupSequence(['create' => ['name', 'email']]);
      
  4. Custom Validators:

    • Create reusable validators by extending Symfony\Component\Validator\ConstraintValidator:
      class UniqueEmailValidator extends ConstraintValidator {
          public function validate($value, Constraint $constraint) {
              if (User::where('email', $value)->exists()) {
                  $this->context->buildViolation($constraint->message)
                      ->addViolation();
              }
          }
      }
      
      Then use it in constraints:
      new UniqueEmail(['message' => 'Email already exists.'])
      

Configuration Quirks

  1. Bundle Auto-Configuration:
    • The bundle auto-registers services in Symfony Flex projects. For manual
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
codifyo/ts-generator-bundle
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor