New in Symfony 6.3: Targeted Value Resolvers


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:

// 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:

// ...
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:

// 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:

// ...
use Symfony\Component\HttpKernel\Attribute\ValueResolver;

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

Sponsor the Symfony project.