@@ -7,33 +7,34 @@ Extending Action Argument Resolving
77..versionadded ::3.1
88 The ``ArgumentResolver `` and value resolvers were introduced in Symfony 3.1.
99
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
10+ In the book, you've learned that you can get the
11+ :class: `Symfony\\ Component\\ HttpFoundation\\ Request ` object via an argument in
12+ your controller. This argument has to be type-hinted by the ``Request `` class
13+ in order to be recognized. This is done via the
1314:class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentResolver `. By
14- creating and registering custom argument value resolvers, you can extend
15- this functionality.
15+ creating and registering custom argument value resolvers, you can extend this
16+ functionality.
1617
1718Functionality Shipped with the HttpKernel
1819-----------------------------------------
1920
2021Symfony ships with four value resolvers in the HttpKernel component:
2122
22- :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentValueResolver \\ ArgumentFromAttributeResolver `
23+ :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentResolver \\ RequestAttributeValueResolver `
2324 Attempts to find a request attribute that matches the name of the argument.
2425
25- :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentValueResolver \\ RequestValueResolver `
26- Injects the current ``Request `` if type-hinted with ``Request ``, or a
27- sub-class thereof .
26+ :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentResolver \\ RequestValueResolver `
27+ Injects the current ``Request `` if type-hinted with ``Request `` or a class
28+ extending `` Request `` .
2829
29- :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentValueResolver \\ DefaultValueResolver `
30+ :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentResolver \\ DefaultValueResolver `
3031 Will set the default value of the argument if present and the argument
3132 is optional.
3233
33- :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentValueResolver \\ VariadicValueResolver `
34- Verifiesin the requestif 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.
34+ :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentResolver \\ VariadicValueResolver `
35+ Verifiesif the request data is an array and will add all of them to the
36+ argument list. When the action is called, the last (variadic) argument will
37+ contain all the values of this array.
3738
3839..note ::
3940
@@ -43,9 +44,10 @@ Symfony ships with four value resolvers in the HttpKernel component:
4344Adding a Custom Value Resolver
4445------------------------------
4546
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::
47+ Adding a new value resolver requires creatign one class and one service
48+ definition. In the next example, you'll create a value resolver to inject the
49+ ``User `` object from the security system. Given you write the following
50+ controller::
4951
5052 namespace AppBundle\Controller;
5153
@@ -56,12 +58,13 @@ object from the security system. Given you write the following action::
5658 {
5759 public function indexAction(User $user)
5860 {
59- return new Response('<html><body> Hello '.$user->getUsername().'!</body></html> ');
61+ return new Response('Hello '.$user->getUsername().'!');
6062 }
6163 }
6264
6365Somehow 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 `.
66+ This can be done by implementing the
67+ :class: `Symfony\\ Component\\ HttpKernel\\ Controller\\ ArgumentValueResolverInterface `.
6568This interface specifies that you have to implement two methods:
6669
6770``supports() ``
@@ -80,7 +83,8 @@ Now that you know what to do, you can implement this interface. To get the
8083current ``User ``, you need the current security token. This token can be
8184retrieved from the token storage::
8285
83- namespace AppBundle\ArgumentValueResolver;
86+ // src/AppBundle/ArgumentResolver/UserValueResolver.php
87+ namespace AppBundle\ArgumentResolver;
8488
8589 use AppBundle\Entity\User;
8690 use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
@@ -123,31 +127,22 @@ must fulfill the following requirements:
123127* A security token must be present;
124128* The value must be an instance of the ``User ``.
125129
126- When all those requirements are met and true is returned, the ``ArgumentResolver ``
127- calls ``resolve() `` with the same values as it called ``supports() ``.
130+ When all those requirements are met and ``true `` is returned, the
131+ ``ArgumentResolver `` calls ``resolve() `` with the same values as it called
132+ ``supports() ``.
128133
129134That's it! Now all you have to do is add the configuration for the service
130135container. This can be done by tagging the service with ``controller.argument_resolver ``
131136and adding a priority.
132137
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-
143138..configuration-block ::
144139
145140 ..code-block ::yaml
146141
147142# app/config/services.yml
148143services :
149144app.value_resolver.user :
150- class :AppBundle\ArgumentValueResolver \UserValueResolver
145+ class :AppBundle\ArgumentResolver \UserValueResolver
151146arguments :
152147 -' @security.token_storage'
153148tags :
@@ -162,7 +157,9 @@ and adding a priority.
162157xsi : schemaLocation =" http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd" >
163158
164159 <services >
165- <service id =" app.value_resolver.user" class =" AppBundle\ArgumentValueResolver\UserValueResolver" >
160+ <service id =" app.value_resolver.user"
161+ class =" AppBundle\ArgumentResolver\UserValueResolver"
162+ >
166163 <argument type =" service" id =" security.token_storage" >
167164 <tag name =" controller.argument_value_resolver" priority =" 50" />
168165 </service >
@@ -176,44 +173,29 @@ and adding a priority.
176173 use Symfony\Component\DependencyInjection\Definition;
177174
178175 $defintion = new Definition(
179- 'AppBundle\ArgumentValueResolver \UserValueResolver',
176+ 'AppBundle\ArgumentResolver \UserValueResolver',
180177 array(new Reference('security.token_storage'))
181178 );
182179 $definition->addTag('controller.argument_value_resolver', array('priority' => 50));
183180 $container->setDefinition('app.value_resolver.user', $definition);
184181
185- Creating an Optional User Resolver
186- ----------------------------------
187-
188- When you want your user tobe optional, e.g. when yourpage is behind a
189- firewall that also allows anonymous authentication, you might notalways
190- have a security user. To get this to work, you only have to change your
191- method signature to ` UserInterface $user = null ` .
182+ While adding a priority is optional, it's recommended to add one to make sure
183+ the expected value is injected. The `` RequestAttributeValueResolver `` has a
184+ priority of 100. As this one is responsible for fetching attributes from the
185+ `` Request ``, it's recommended totrigger yourcustom value resolver with a
186+ lower priority. This makes sure the argument resolvers are nottriggered when
187+ the attribute is present. For instance, when passing the user along a
188+ subrequests .
192189
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::
190+ ..tip ::
199191
200- namespace Symfony\Component\HttpKernel\Controller\ArgumentResolver;
192+ As you see in the ``UserValueResolver::supports() `` method, the user may
193+ not be available (e.g. when the controller is not behind a firewall). In
194+ these cases, the resolver will not be executed. If no argument value is
195+ resolved, an exception will be throwed.
201196
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- }
197+ To prevent this, you can add a default value in the controller (e.g. ``User
198+ $user = null ``). The ``DefaultValueResolver `` is executed as last resolver
199+ and will use the default value if no value is resolved already.
218200
219201.. _`yield` :http://php.net/manual/en/language.generators.syntax.php