Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[HttpKernel] Allow injecting query and request parameters in controllers by typing them with #[QueryParameter] or #[RequestParameter] attribute #49134

Open
wants to merge 1 commit into
base: 6.3
Choose a base branch
from

Conversation

ruudk
Copy link
Contributor

@ruudk ruudk commented Jan 27, 2023

Q A
Branch? 6.3
Bug fix? no
New feature? yes
Deprecations? no
Tickets
License MIT
Doc PR

We increased the PHPStan level from 8 to 9. This lead to some problems when working with query or request parameters.

For example:

$firstName = $request->get('firstName');

Because Symfony types Request::get() as mixed there is no type safety and you have to assert everything manually.

We then learned that we shouldn't use Request::get() but use the explicit parameter bag like:

$request->query->get('firstName');

This ParameterBag is used for request and query. It contains interesting methods like:

$request->query->getAlpha('firstName') : string;
$request->query->getInt('age') : int;

This has the benefit that now the returned value is of a correct type.

Why aren't we being explicit by requiring the parameters and their types in the controller action instead?

Luckily Symfony has a concept called ValueResolver.

It allows you to do dynamically alter what is injected into a controller action.

So in this PR, we introduces 2 new attributes: #[QueryParameter] and #[RequestParameter] that can be used in controller arguments.

It allows you to define which parameters your controller is using and which type they should be. For example:

#[Route(path: '/', name: 'admin_dashboard')]
public function indexAction(
  #[QueryParameter]
  array $ids,
  #[QueryParameter]
  string $firstName,
  #[QueryParameter]
  bool $required,
  #[QueryParameter]
  int $age,
  #[QueryParameter]
  string $category = '',
  #[QueryParameter]
  ?string $theme = null,
)

When requesting /?ids[]=1&ids[]=2&firstName=Ruud&required=3&age=123 you'll get:

$ids = ['1', '2']
$firstName = "Ruud"
$required = false
$age = 123
$category = ''
$theme = null

It even supports variadic arguments like this:

#[Route(path: '/', name: 'admin_dashboard')]
public function indexAction(
  #[QueryParameter]
  string ...$ids,
)

When requesting /?ids[]=111&ids[]=222 the $ids argument will have an array with values ['111','222'].

Unit testing the controller now also becomes a bit easier, as you only have to pass the required parameters instead of constructing the Request object.

@carsonbot carsonbot added this to the 6.3 milestone Jan 27, 2023
@ruudk ruudk changed the title Allow injecting query and request parameters in controllers by typing them with #[QueryParameter] or #[RequestParameter] attribute [HttpKernel] Allow injecting query and request parameters in controllers by typing them with #[QueryParameter] or #[RequestParameter] attribute Jan 27, 2023
@ruudk ruudk force-pushed the query-request-value-resolver branch 2 times, most recently from 7fe4558 to 427874e Compare Jan 27, 2023
We increased the PHPStan level from 8 to 9. This lead to some problems when working with query or request parameters.

For example:
```php
$firstName = $request->get('firstName');
```

Because Symfony types `Request::get()` as `mixed` there is no type safety and you have to assert everything manually.

We then learned that we shouldn't use `Request::get()` but use the explicit parameter bag like:
```php
$request->query->get('firstName');
```

This `ParameterBag` is used for `request` and `query`. It contains interesting methods like:
```php
$request->query->getAlpha('firstName') : string;
$request->query->getInt('age') : int;
```

This has the benefit that now the returned value is of a correct type.

Why aren't we being explicit by requiring the parameters and their types in the controller action instead?

Luckily Symfony has a concept called [ValueResolver](https://symfony.com/doc/current/controller/value_resolver.html).

It allows you to do dynamically alter what is injected into a controller action.

So in this PR, we introduces 2 new attributes: `#[QueryParameter]` and `#[RequestParameter]` that
can be used in controller arguments.

It allows you to define which parameters your controller is using and which type they should be. For example:

```php
#[Route(path: '/', name: 'admin_dashboard')]
public function indexAction(
  #[QueryParameter]
  array $ids,
  #[QueryParameter]
  string $firstName,
  #[QueryParameter]
  bool $required,
  #[QueryParameter]
  int $age,
  #[QueryParameter]
  string $category = '',
  #[QueryParameter]
  ?string $theme = null,
)
```

When requesting `/?ids[]=1&ids[]=2&firstName=Ruud&required=3&age=123` you'll get:
```
$ids = ['1', '2']
$firstName = "Ruud"
$required = false
$age = 123
$category = ''
$theme = null
```

It even supports variadic arguments like this:
```php
#[Route(path: '/', name: 'admin_dashboard')]
public function indexAction(
  #[QueryParameter]
  string ...$ids,
)
```

When requesting `/?ids[]=111&ids[]=222` the `$ids` argument will have an array with values ['111','222'].

Unit testing the controller now also becomes a bit easier, as you only have to pass the required parameters instead of constructing the `Request` object.
@ruudk ruudk force-pushed the query-request-value-resolver branch from 427874e to 878ddd5 Compare Jan 27, 2023
@alamirault
Copy link
Contributor

A like the idea, deporting some basic checks and use type hint

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants