Skip to content

Commit 814e336

Browse files
committed
Merge branch '6.3' into 6.4
* 6.3: Document target value resolvers
2 parents cd28f8a + 9d999ce commit 814e336

File tree

2 files changed

+117
-7
lines changed

2 files changed

+117
-7
lines changed

controller/value_resolver.rst

Lines changed: 115 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,61 @@ PSR-7 Objects Resolver:
213213
:class:`Psr\\Http\\Message\\RequestInterface` or :class:`Psr\\Http\\Message\\MessageInterface`.
214214
It requires installing :doc:`the PSR-7 Bridge </components/psr7>` component.
215215

216+
Managing Value Resolvers
217+
------------------------
218+
219+
For each argument, every resolver tagged with ``controller.argument_value_resolver``
220+
will be called until one provides a value. The order in which they are called depends
221+
on their priority. For example, the ``SessionValueResolver`` will be called before the
222+
``DefaultValueResolver`` because its priority is higher. This allows to write e.g.
223+
``SessionInterface $session = null`` to get the session if there is one, or ``null``
224+
if there is none.
225+
226+
In that specific case, you don't need any resolver running before
227+
``SessionValueResolver``, so skipping them would not only improve performance,
228+
but also prevent one of them providing a value before ``SessionValueResolver``
229+
has a chance to.
230+
231+
The :class:`Symfony\\Component\\HttpKernel\\Attribute\\ValueResolver` attribute
232+
lets you do this by "targeting" the resolver you want::
233+
234+
// src/Controller/SessionController.php
235+
namespace App\Controller;
236+
237+
use Symfony\Component\HttpFoundation\Response;
238+
use Symfony\Component\HttpFoundation\Session\SessionInterface;
239+
use Symfony\Component\HttpKernel\Attribute\ValueResolver;
240+
use Symfony\Component\HttpKernel\Controller\ArgumentResolver\SessionValueResolver;
241+
use Symfony\Component\Routing\Annotation\Route;
242+
243+
class SessionController
244+
{
245+
#[Route('/')]
246+
public function __invoke(
247+
#[ValueResolver(SessionValueResolver::class)]
248+
SessionInterface $session = null
249+
): Response
250+
{
251+
// ...
252+
}
253+
}
254+
255+
.. versionadded:: 6.3
256+
257+
The ``ValueResolver`` attribute was introduced in Symfony 6.3.
258+
259+
In the example above, the ``SessionValueResolver`` will be called first because
260+
it is targeted. The ``DefaultValueResolver`` will be called next if no value has
261+
been provided; that's why you can assign ``null`` as ``$session``'s default value.
262+
263+
You can target a resolver by passing its name as ``ValueResolver``'s first argument.
264+
For convenience, built-in resolvers' name are their FQCN.
265+
266+
A targeted resolver can also be disabled by passing ``ValueResolver``'s ``$disabled``
267+
argument to ``true``; this is how :ref:`MapEntity allows to disable the
268+
EntityValueResolver for a specific controller <doctrine-entity-value-resolver>`.
269+
Yes, ``MapEntity`` extends ``ValueResolver``!
270+
216271
Adding a Custom Value Resolver
217272
------------------------------
218273

@@ -297,8 +352,13 @@ When those requirements are met, the method creates a new instance of the
297352
custom value object and returns it as the value for this argument.
298353

299354
That's it! Now all you have to do is add the configuration for the service
300-
container. This can be done by tagging the service with ``controller.argument_value_resolver``
301-
and adding a priority:
355+
container. This can be done by adding one of the following tags to your value resolver.
356+
357+
``controller.argument_value_resolver``
358+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
359+
360+
This tag is automatically added to every service implementing ``ValueResolverInterface``,
361+
but you can set it yourself to change its ``priority`` or ``name`` attributes.
302362

303363
.. configuration-block::
304364

@@ -313,7 +373,9 @@ and adding a priority:
313373
314374
App\ValueResolver\BookingIdValueResolver:
315375
tags:
316-
- { name: controller.argument_value_resolver, priority: 150 }
376+
- controller.argument_value_resolver:
377+
name: booking_id
378+
priority: 150
317379
318380
.. code-block:: xml
319381
@@ -330,7 +392,7 @@ and adding a priority:
330392
<!-- ... -->
331393
332394
<service id="App\ValueResolver\BookingIdValueResolver">
333-
<tag name="controller.argument_value_resolver" priority="150"/>
395+
<tag name="booking_id" priority="150">controller.argument_value_resolver</tag>
334396
</service>
335397
</services>
336398
@@ -347,7 +409,7 @@ and adding a priority:
347409
$services = $containerConfigurator->services();
348410
349411
$services->set(BookingIdValueResolver::class)
350-
->tag('controller.argument_value_resolver', ['priority' => 150])
412+
->tag('controller.argument_value_resolver', ['name' => 'booking_id', 'priority' => 150])
351413
;
352414
};
353415
@@ -364,3 +426,51 @@ command to see which argument resolvers are present and in which order they run:
364426
.. code-block:: terminal
365427
366428
$ php bin/console debug:container debug.argument_resolver.inner --show-arguments
429+
430+
You can also configure the name passed to the ``ValueResolver`` attribute to target
431+
your resolver. Otherwise it will default to the service's id.
432+
433+
``controller.targeted_value_resolver``
434+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
435+
436+
Set this tag if you want your resolver to be called only if it is targeted by a
437+
``ValueResolver`` attribute. Like ``controller.argument_value_resolver``, you
438+
can customize the name by which your resolver can be targeted.
439+
440+
As an alternative, you can add the
441+
:class:`Symfony\\Component\\HttpKernel\\Attribute\\AsTargetedValueResolver` attribute
442+
to your resolver and pass your custom name as its first argument::
443+
444+
// src/ValueResolver/IdentifierValueResolver.php
445+
namespace App\ValueResolver;
446+
447+
use Symfony\Component\HttpKernel\Attribute\AsTargetedValueResolver;
448+
use Symfony\Component\HttpKernel\Controller\ValueResolverInterface;
449+
450+
#[AsTargetedValueResolver('booking_id')]
451+
class BookingIdValueResolver implements ValueResolverInterface
452+
{
453+
// ...
454+
}
455+
456+
You can then pass this name as ``ValueResolver``'s first argument to target your resolver::
457+
458+
// src/Controller/BookingController.php
459+
namespace App\Controller;
460+
461+
use App\Reservation\BookingId;
462+
use Symfony\Component\HttpFoundation\Response;
463+
use Symfony\Component\HttpKernel\Attribute\ValueResolver;
464+
465+
class BookingController
466+
{
467+
public function index(#[ValueResolver('booking_id')] BookingId $id): Response
468+
{
469+
// ... do something with $id
470+
}
471+
}
472+
473+
.. versionadded:: 6.3
474+
475+
The ``controller.targeted_value_resolver`` tag and ``AsTargetedValueResolver``
476+
attribute were introduced in Symfony 6.3.

reference/attributes.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ HttpKernel
5454
~~~~~~~~~~
5555

5656
* :doc:`AsController </controller/service>`
57-
* :class:`Symfony\\Component\\HttpKernel\\Attribute\\AsPinnedValueResolver`
57+
* :ref:`AsTargetedValueResolver <controller-targeted-value-resolver>`
5858
* :ref:`Cache <http-cache-expiration-intro>`
5959
* :ref:`MapDateTime <functionality-shipped-with-the-httpkernel>`
6060
* :ref:`MapQueryParameter <controller_map-request>`
6161
* :ref:`MapQueryString <controller_map-request>`
6262
* :ref:`MapRequestPayload <controller_map-request>`
63-
* :class:`Symfony\\Component\\HttpKernel\\Attribute\\ValueResolver`
63+
* :ref:`ValueResolver <managing-value-resolvers>`
6464
* :ref:`WithHttpStatus <framework_exceptions>`
6565
* :ref:`WithLogLevel <framework_exceptions>`
6666

0 commit comments

Comments
 (0)