ekreative/query-parameter-bundle
composer require ekreative/query-parameter-bundle
new Ekreative\QueryParameterBundle\EkreativeQueryParameterBundle() to AppKernel.php (or config/bundles.php for Symfony 4+).sensio/framework-extra-bundle and Symfony’s OptionResolver/PropertyAccess are installed.Validate a boolean query parameter in a controller:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Ekreative\QueryParameterBundle\Annotation\QueryParameter;
/**
* @Route("/search")
* @QueryParameter("active", type="boolean", options={"required" = false, "default" = false})
*/
public function searchAction(bool $active) {
// $active is now a validated boolean (true/false)
}
Use @QueryParameter for simple, single-field validation:
/**
* @QueryParameter("page", type="integer", options={"required" = true, "min" = 1})
*/
public function listAction(int $page) {
// $page is guaranteed to be an integer ≥ 1
}
@QueryModel)For multi-field validation, create a DTO (e.g., Filter class) and annotate:
// src/Filter/TestFilter.php
class TestFilter {
/** @QueryParameter("min_age", type="integer", options={"default" = 18}) */
public $minAge;
/** @QueryParameter("is_active", type="boolean") */
public $isActive;
}
Controller:
/**
* @QueryModel("filter", class="AppBundle\Filter\TestFilter")
*/
public function advancedSearch(TestFilter $filter) {
// $filter->minAge and $filter->isActive are validated
}
Reuse validation logic in forms:
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
// In a form builder:
$builder->add('page', IntegerType::class, [
'constraints' => [
new Assert\Type(['type' => 'integer']),
new Assert\GreaterThan(['value' => 0]),
],
]);
Tip: Mirror @QueryParameter constraints in forms for consistency.
Combine with Sensio’s @ParamConverter for hybrid validation:
/**
* @Route("/user/{id}")
* @QueryParameter("sort", type="string", options={"allowed_values" = {"name", "email"}})
* @ParamConverter("user", converter="doctrine")
*/
public function userAction(User $user, string $sort) {
// $sort is validated against ["name", "email"]
}
Validate nested query parameters for API pagination/sorting:
/**
* @QueryModel("pagination", class="AppBundle\Filter\PaginationFilter")
*/
public function apiListAction(PaginationFilter $pagination) {
// $pagination->page, $pagination->limit, $pagination->sort are validated
}
Missing Dependencies:
sensio/framework-extra-bundle is missing, annotations won’t work. Install via:
composer require sensio/framework-extra-bundle
symfony/flex or manually add to config/bundles.php.Type Mismatches:
OptionResolver, so types must match PHP’s strict typing (e.g., integer ≠ int in older PHP versions).type="integer" (string) or type="bool" (boolean) explicitly.Annotation Override:
@QueryParameter overrides route parameters. If you have:
@Route("/user/{id}")
@QueryParameter("id", type="integer")
The route {id} will be ignored in favor of the query string.Default Values:
default values are not merged with route defaults. Specify one or the other:
// Bad: May conflict
@Route("/user/{id}", defaults={"id" = 1})
@QueryParameter("id", type="integer", options={"default" = 2})
// Good: Explicit choice
@QueryParameter("id", type="integer", options={"default" = 1})
Circular References:
QueryModel classes (e.g., FilterA referencing FilterB which references FilterA).Enable Annotation Debugging:
Add to config/packages/framework.yaml:
framework:
annotations:
cache: null # Disable cache to see raw annotations
Then check var/log/dev.log for parsed annotations.
Validation Errors:
InvalidArgumentException. Catch globally in a listener:
// src/EventListener/QueryValidationListener.php
public function onKernelException(GetResponseForExceptionEvent $event) {
$exception = $event->getException();
if ($exception instanceof \InvalidArgumentException &&
strpos($exception->getMessage(), 'QueryParameter') !== false) {
$event->setResponse(new JsonResponse(['error' => $exception->getMessage()], 400));
}
}
Register in services.yaml:
services:
App\EventListener\QueryValidationListener:
tags:
- { name: kernel.event_listener, event: kernel.exception }
Type-Specific Quirks:
datetime: Expects YYYY-MM-DD or ISO 8601 strings. Use options={"format" = "Y-m-d H:i:s"} for custom formats.double: May parse 1.23 as 1 in some PHP versions. Use options={"scale" = 2} to enforce precision.Custom Validators:
Extend the bundle’s QueryParameterValidator (located in Ekreative\QueryParameterBundle\Validator\QueryParameterValidator) to add custom rules:
// src/Validator/CustomQueryValidator.php
use Ekreative\QueryParameterBundle\Validator\QueryParameterValidatorInterface;
class CustomQueryValidator implements QueryParameterValidatorInterface {
public function validate($value, array $options) {
if ($options['type'] === 'custom' && $value !== 'allowed') {
throw new \InvalidArgumentException('Custom validation failed');
}
return $value;
}
}
Register as a service:
services:
App\Validator\CustomQueryValidator:
tags:
- { name: ekreative.query_parameter.validator, type: custom }
Override Default Types:
Replace the default validator for a type (e.g., integer) by implementing QueryParameterValidatorInterface and tagging it with the desired type:
services:
App\Validator\CustomIntegerValidator:
tags:
- { name: ekreative.query_parameter.validator, type: integer }
Event Dispatching:
Listen for ekreative.query_parameter.validated events to log or modify validated values:
use Ekreative\QueryParameterBundle\Event\QueryParameterValidatedEvent;
public function onQueryParameterValidated(QueryParameterValidatedEvent $event) {
$event->setValue(strtoupper($event->getValue())); // Example: Force uppercase
}
Register:
services:
App\EventListener\QueryParameterListener:
tags:
- { name: kernel.event_listener, event: ekreative.query_parameter.validated }
Configuration Overrides:
Override bundle defaults in config/packages/ekreative_query_parameter.yaml:
ekreative_query_parameter:
strict_types: true # Enable strict type checking
default_locale: en_US # For datetime parsing
How can I help you explore Laravel packages today?