Mathieu
Contributed by Mathieu in #48992

In Symfony, argument value resolvers allow to inject certain values in the arguments of the controllers. For example, if you type-hint any controller argument with the Request class from HttpFoundation, Symfony injects the object that represents the current request.

Symfony provides lots of built-in resolvers to inject services, the session, UID values, default PHP values, etc. In Symfony 6.3 we're improving this feature to make it more powerful. First, we're introducing a new ValueResolver attribute to explicitly select the resolver to use.

Consider the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/Controller/SessionController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;

class SessionController
{
    #[Route('/')]
    public function __invoke(SessionInterface $session = null): Response
    {
        // ...
    }
}

Symfony will call all built-in argument resolvers by priority until one of them provides a value for this argument. In this example, the SessionValueResolver (priority 50) will be called before the DefaultValueResolver (priority -100). That's why the $session argument will have either the current Session object or a null value.

If you know that in your application there will always be a session, then you could do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ...
use Symfony\Component\HttpKernel\Attribute\ValueResolver;
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver;

class SessionController
{
    #[Route('/')]
    public function __invoke(
        #[ValueResolver(SessionValueResolver::class)]
        SessionInterface $session
    ): Response
    {
        // ...
    }
}

The new ValueResolver attribute allows you to explicitly tell Symfony which resolver should be used to get the value of this argument. For convenience, the name of all built-in resolvers is their FQCN (e.g. SessionValueResolver::class).

In addition to this, we've added another new AsTargetedValueResolver attribute to create resolvers that can only be called explicitly. Consider the following argument resolver which transforms booking id values into Booking objects:

1
2
3
4
5
6
7
8
9
10
11
// src/ValueResolver/IdentifierValueResolver.php
namespace App\ValueResolver;

use Symfony\Component\HttpKernel\Attribute\AsTargetedValueResolver;
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;

#[AsTargetedValueResolver('booking_id')]
class BookingIdValueResolver implements ValueResolverInterface
{
    // ...
}

Instead of letting Symfony call this resolver for all arguments of all controllers, the new #[AsTargetedValueResolver] attribute tells Symfony to only use this resolver if it's called explicitly. Therefore, it will only be used in cases like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...
use Symfony\Component\HttpKernel\Attribute\ValueResolver;

class BookingController
{
    #[Route('/booking/{id}')]
    public function show(
        #[ValueResolver('booking_id')]
        Booking $booking
    ): Response
    {
        // ...
    }
}
Published in #Living on the edge