Skip to content

Commit 0339a40

Browse files
author
Iltar van der Berg
committed
Added docs about ArgumentValueResolvers
1 parent d7724dd commit 0339a40

File tree

3 files changed

+159
-0
lines changed

3 files changed

+159
-0
lines changed
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
.. index::
2+
single: Controller; Argument Value Resolvers
3+
4+
Extending Action Argument Resolving
5+
===================================
6+
7+
.. versionadded:: 3.1
8+
The ``ArgumentResolver`` and value resolvers are added in Symfony 3.1.
9+
10+
In the book, you've learned that you can add the :class:`Symfony\\Component\\HttpFoundation\\Request`
11+
as action argument and it will be injected into the method. This is done via the
12+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver`. The ``ArgumentResolver`` uses
13+
several value resolvers which allow you to extend the functionality.
14+
15+
16+
Functionality Shipped With The HttpKernel
17+
-----------------------------------------
18+
19+
Symfony ships with four value resolvers in the HttpKernel:
20+
- The :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\ArgumentFromAttributeResolver`
21+
attempts to find a request attribute that matches the name of the argument.
22+
23+
- The :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\RequestValueResolver`
24+
injects the current ``Request`` if type-hinted with ``Request``, or a sub-class thereof.
25+
26+
- The :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\DefaultValueResolver`
27+
will set the default value of the argument if present and the argument is optional.
28+
29+
- The :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\VariadicValueResolver`
30+
verifies in the request if your data is an array and will add all of them to the argument list.
31+
When the action is called, the last (variadic) argument will contain all the values of this array.
32+
33+
.. note::
34+
In older versions of Symfony this logic was all resolved within the ``ControllerResolver``. The
35+
old functionality is moved to the ``LegacyArgumentResolver``, which contains the previously
36+
used resolving logic.
37+
38+
Adding a New Value Resolver
39+
---------------------------
40+
41+
Adding a new value resolver requires one class and one service defintion. In our next example, we
42+
will be creating a shortcut to inject the ``User`` object from our security. Given we write the following
43+
action::
44+
45+
namespace AppBundle\Controller;
46+
47+
class UserController
48+
{
49+
public function indexAction(User $user)
50+
{
51+
return new Response('<html><body>Hello '.$user->getUsername().'!</body></html>');
52+
}
53+
}
54+
55+
Somehow we will have to get the ``User`` object and inject it into our action. This can be done
56+
by implementing the :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`.
57+
This interface specifies that we have to implement two methods::
58+
59+
interface ArgumentValueResolverInterface
60+
{
61+
public function supports(Request $request, ArgumentMetadata $argument);
62+
public function resolve(Request $request, ArgumentMetadata $argument);
63+
}
64+
65+
- The ``supports()`` method is used to check whether the resolver supports the given argument. It will
66+
only continue if it returns ``true``.
67+
68+
- The ``resolve()`` method will be used to resolve the actual value just acknowledged by
69+
``supports()``. Once a value is resolved you can ``yield`` the value to the ``ArgumentResolver``.
70+
71+
- The ``Request`` object is the current ``Request`` which would also be injected into your
72+
action in the forementioned functionality.
73+
74+
- The :class:``Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata`` represents
75+
information retrieved from the method signature for the current argument it's trying to resolve.
76+
77+
.. note::
78+
The ``ArgumentMetadata`` is a simple data container created by the
79+
:class:``Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory``. This
80+
factory will work on every supported php version but might give different results. E.g. the
81+
``isVariadic()`` will never return true on php 5.5 and only on php 7.0 and higher it will give
82+
you basic types when calling ``getType()``.
83+
84+
Now that we know what to do, we can implement this interface. In order to get the current ``User``,
85+
we will have to get it from the ``TokenInterface`` which is in the ``TokenStorageInterface``::
86+
87+
namespace AppBundle\ArgumentValueResolver;
88+
89+
use AppBundle\User;
90+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
91+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
92+
93+
class UserValueResolver implements ArgumentValueResolverInterface
94+
{
95+
private $tokenStorage;
96+
97+
public function __construct(TokenStorageInterface $tokenStorage)
98+
{
99+
$this->tokenStorage = $tokenStorage;
100+
}
101+
102+
public function supports(Request $request, ArgumentMetadata $argument)
103+
{
104+
return ($token = $this->tokenStorage->getToken()) && $token->getUser() instanceof User;
105+
}
106+
107+
public function resolve(Request $request, ArgumentMetadata $argument)
108+
{
109+
yield $this->tokenStorage->getToken()->getUser();
110+
}
111+
}
112+
113+
This was pretty simple, now all we have to do is add the configuration for the service container. This
114+
can be done by tagging the service with ``kernel.argument_resolver`` and adding a priority.
115+
116+
.. note::
117+
While adding a priority is optional, it's recommended to add one to make sure the expected
118+
value is injected. The ``ArgumentFromAttributeResolver`` has a priority of 100. As this
119+
one is responsible for fetching attributes from the ``Request``, it's also recommended to
120+
trigger your custom value resolver with a lower priority. This makes sure the argument
121+
resolvers are not triggered in (e.g.) subrequests if you pass your user along:
122+
``{{ render(controller('AppBundle:User:index', {'user', app.user})) }}``.
123+
124+
.. configuration-block::
125+
126+
.. code-block:: yaml
127+
128+
# app/config/services.yml
129+
services:
130+
app.value_resolver.user:
131+
class: AppBundle\ArgumentValueResolver\UserValueResolver
132+
arguments:
133+
- '@security.token_storage'
134+
tags:
135+
- { name: kernel.argument_resolver, priority: 50 }
136+
137+
.. code-block:: xml
138+
139+
<!-- app/config/services.xml -->
140+
<services>
141+
<service id="app.value_resolver.user" class="AppBundle\ArgumentValueResolver\UserValueResolver">
142+
<argument type="service" id="security.token_storage">
143+
<tag name="kernel.argument_resolver" priority="50" />
144+
</service>
145+
</services>
146+
147+
.. code-block:: php
148+
149+
// app/config/services.php
150+
use Symfony\Component\DependencyInjection\Definition;
151+
152+
$defintion = new Definition(
153+
'AppBundle\ArgumentValueResolver\UserValueResolver',
154+
array(new Reference('security.token_storage'))
155+
);
156+
$definition->addTag('kernel.argument_resolver', array('priority' => 50));
157+
$container->setDefinition('app.value_resolver.user', $definition);

cookbook/controller/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ Controller
77
error_pages
88
service
99
upload_file
10+
argument_value_resolver

cookbook/map.rst.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
* :doc:`/cookbook/controller/error_pages`
5757
* :doc:`/cookbook/controller/service`
5858
* :doc:`/cookbook/controller/upload_file`
59+
* :doc:`/cookbook/controller/argument_value_resolver`
5960

6061
* **Debugging**
6162

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy