8000 feature #6438 Added docs about ArgumentValueResolvers (iltar) · symfony/symfony-docs@ae73303 · GitHub
[go: up one dir, main page]

Skip to content

Commit ae73303

Browse files
committed
feature #6438 Added docs about ArgumentValueResolvers (iltar)
This PR was submitted for the master branch but it was merged into the 3. 8000 1 branch instead (closes #6438). Discussion ---------- Added docs about ArgumentValueResolvers | Q | A | ------------- | --- | Doc fix? | no | New docs? | yes | Applies to | 3.1 | Fixed tickets | ~ Adds the documentation for the new `ArgumentValueResolver` feature from symfony/symfony#18308. Commits ------- f22dc96 Added docs about ArgumentValueResolvers
2 parents 0db3a92 + f22dc96 commit ae73303

File tree

3 files changed

+221
-0
lines changed

3 files changed

+221
-0
lines changed
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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 were introduced in Symfony 3.1.
9+
10+
In the book, you've learned that you can get the :class:`Symfony\\Component\\HttpFoundation\\Request`
11+
object via an argument in your controller. This argument has to be type-hinted
12+
by the ``Request`` class in order to be recognized. This is done via the
13+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver`. By
14+
creating and registering custom argument value resolvers, you can extend
15+
this functionality.
16+
17+
Functionality Shipped with the HttpKernel
18+
-----------------------------------------
19+
20+
Symfony ships with four value resolvers in the HttpKernel component:
21+
22+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\ArgumentFromAttributeResolver`
23+
Attempts to find a request attribute that matches the name of the argument.
24+
25+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\RequestValueResolver`
26+
Injects the current ``Request`` if type-hinted with ``Request``, or a
27+
sub-class thereof.
28+
29+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\DefaultValueResolver`
30+
Will set the default value of the argument if present and the argument
31+
is optional.
32+
33+
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\VariadicValueResolver`
34+
Verifies in the request if your data is an array and will add all of
35+
them to the argument list. When the action is called, the last (variadic)
36+
argument will contain all the values of this array.
37+
38+
.. note::
39+
40+
Prior to Symfony 3.1, this logic was resolved within the ``ControllerResolver``.
41+
The old functionality is rewritten to the aforementioned value resolvers.
42+
43+
Adding a Custom Value Resolver
44+
------------------------------
45+
46+
Adding a new value resolver requires one class and one service defintion.
47+
In the next example, you'll create a value resolver to inject the ``User``
48+
object from the security system. Given you write the following action::
49+
50+
namespace AppBundle\Controller;
51+
52+
use AppBundle\Entity\User;
53+
use Symfony\Component\HttpFoundation\Response;
54+
55+
class UserController
56+
{
57+
public function indexAction(User $user)
58+
{
59+
return new Response('<html><body>Hello '.$user->getUsername().'!</body></html>');
60+
}
61+
}
62+
63+
Somehow you will have to get the ``User`` object and inject it into the controller.
64+
This can be done by implementing the :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`.
65+
This interface specifies that you have to implement two methods:
66+
67+
``supports()``
68+
This method is used to check whether the value resolver supports the
69+
given argument. ``resolve()`` will only be executed when this returns ``true``.
70+
``resolve()``
71+
This method will resolve the actual value for the argument. Once the value
72+
is resolved, you must `yield`_ the value to the ``ArgumentResolver``.
73+
74+
Both methods get the ``Request`` object, which is the current request, and an
75+
:class:`Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata`
76+
instance. This object contains all information retrieved from the method signature
77+
for the current argument.
78+
79+
Now that you know what to do, you can implement this interface. To get the
80+
current ``User``, you need the current security token. This token can be
81+
retrieved from the token storage::
82+
83+
namespace AppBundle\ArgumentValueResolver;
84+
85+
use AppBundle\Entity\User;
86+
use Symfony\Componen 23DA t\HttpKernel\Controller\ArgumentValueResolverInterface;
87+
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
88+
89+
class UserValueResolver implements ArgumentValueResolverInterface
90+
{
91+
private $tokenStorage;
92+
93+
public function __construct(TokenStorageInterface $tokenStorage)
94+
{
95+
$this->tokenStorage = $tokenStorage;
96+
}
97+
98+
public function supports(Request $request, ArgumentMetadata $argument)
99+
{
100+
if (User::class !== $argument->getType()) {
101+
return false;
102+
}
103+
104+
$token = $this->tokenStorage->getToken();
105+
106+
if (!$token instanceof TokenInterface) {
107+
return false;
108+
}
109+
110+
return $token->getUser() instanceof User;
111+
}
112+
113+
public function resolve(Request $request, ArgumentMetadata $argument)
114+
{
115+
yield $this->tokenStorage->getToken()->getUser();
116+
}
117+
}
118+
119+
In order to get the actual ``User`` object in your argument, the given value
120+
must fulfill the following requirements:
121+
122+
* An argument must be type-hinted as ``User`` in your action method signature;
123+
* A security token must be present;
124+
* The value must be an instance of the ``User``.
125+
126+
When all those requirements are met and true is returned, the ``ArgumentResolver``
127+
calls ``resolve()`` with the same values as it called ``supports()``.
128+
129+
That's it! Now all you have to do is add the configuration for the service
130+
container. This can be done by tagging the service with ``controller.argument_resolver``
131+
and adding a priority.
132+
133+
.. note::
134+
135+
While adding a priority is optional, it's recommended to add one to
136+
make sure the expected value is injected. The ``ArgumentFromAttributeResolver``
137+
has a priority of 100. As this one is responsible for fetching attributes
138+
from the ``Request``, it's also recommended to trigger your custom value
139+
resolver with a lower priority. This makes sure the argument resolvers
140+
are not triggered in (e.g.) subrequests if you pass your user along:
141+
``{{ render(controller('AppBundle:User:index', {'user', app.user})) }}``.
142+
143+
.. configuration-block::
144+
145+
.. code-block:: yaml
146+
147+
# app/config/services.yml
148+
services:
149+
app.value_resolver.user:
150+
class: AppBundle\ArgumentValueResolver\UserValueResolver
151+
arguments:
152+
- '@security.token_storage'
153+
tags:
154+
- { name: controller.argument_value_resolver, priority: 50 }
155+
156+
.. code-block:: xml
157+
158+
<!-- app/config/services.xml -->
159+
<?xml version="1.0" encoding="UTF-8" ?>
160+
<container xmlns="http://symfony.com/schema/dic/services"
161+
xmlns:xsi="'http://www.w3.org/2001/XMLSchema-Instance"
162+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
163+
164+
<services>
165+
<service id="app.value_resolver.user" class="AppBundle\ArgumentValueResolver\UserValueResolver">
166+
<argument type="service" id="security.token_storage">
167+
<tag name="controller.argument_value_resolver" priority="50" />
168+
</service>
169+
</services>
170+
171+
</container>
172+
173+
.. code-block:: php
174+
175+
// app/config/services.php
176+
use Symfony\Component\DependencyInjection\Definition;
177+
178+
$defintion = new Definition(
179+
'AppBundle\ArgumentValueResolver\UserValueResolver',
180+
array(new Reference('security.token_storage'))
181+
);
182+
$definition->addTag('controller.argument_value_resolver', array('priority' => 50));
183+
$container->setDefinition('app.value_resolver.user', $definition);
184+
185+
Creating an Optional User Resolver
186+
----------------------------------
187+
188+
When you want your user to be optional, e.g. when your page is behind a
189+
firewall that also allows anonymous authentication, you might not always
190+
have a security user. To get this to work, you only have to change your
191+
method signature to `UserInterface $user = null`.
192+
193+
When you take the ``UserValueResolver`` from the previous example, you can
194+
see there is no logic in case of failure to comply to the requirements. Default
195+
values are defined in the signature and are available in the ``ArgumentMetadata``.
196+
When a default value is available and there are no resolvers that support
197+
the given value, the ``DefaultValueResolver`` is triggered. This Resolver
198+
takes the default value of your argument and yields it to the argument list::
199+
200+
namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver;
201+
202+
use Symfony\Component\HttpFoundation\Request;
203+
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
204+
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
205+
206+
final class DefaultValueResolver implements ArgumentValueResolverInterface
207+
{
208+
public function supports(Request $request, ArgumentMetadata $argument)
209+
{
210+
return $argument->hasDefaultValue();
211+
}
212+
213+
public function resolve(Request $request, ArgumentMetadata $argument)
214+
{
215+
yield $argument->getDefaultValue();
216+
}
217+
}
218+
219+
.. _`yield`: http://php.net/manual/en/language.generators.syntax.php

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)
0