10000 Added docs about ArgumentValueResolvers by linaori · Pull Request #6438 · symfony/symfony-docs · GitHub
[go: up one dir, main page]

Skip to content

Added docs about ArgumentValueResolvers #6438

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Prev Previous commit
Next Next commit
Respecting the line length
  • Loading branch information
Iltar van der Berg committed Apr 29, 2016
commit 4c6ed2abfbea3aa5a2f1acb15a54336ee2b9fd0a
84 changes: 47 additions & 37 deletions cookbook/controller/argument_value_resolver.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ Extending Action Argument Resolving
The ``ArgumentResolver`` and value resolvers are added in Symfony 3.1.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"are added" => "were introduced" ?


In the book, you've learned that you can get the :class:`Symfony\\Component\\HttpFoundation\\Request`
object by adding a ``Request`` argument to your controller. This is done via the
:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver`. By creating and registering custom
argument value resolvers, you can extend this functionality.
object by adding a ``Request`` argument to your controller. This is done
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"adding a type hinted $request argument" ?

via the :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolver`.
By creating and registering custom argument value resolvers, you can extend
this functionality.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra empty line

Functionality Shipped With The HttpKernel
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it be something like this then: Functionality Shipped with the HttpKernel? It looks odd to have with but The

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right.

-----------------------------------------
Expand All @@ -21,26 +22,29 @@ Symfony ships with four value resolvers in the HttpKernel:
Attempts to find a request attribute that matches the name of the argument.

:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\RequestValueResolver`
Injects the current ``Request`` if type-hinted with ``Request``, or a sub-class thereof.
Injects the current ``Request`` if type-hinted with ``Request``, or a
sub-class thereof.

10000 :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\DefaultValueResolver`
Will set the default value of the argument if present and the argument is optional.
Will set the default value of the argument if present and the argument
is optional.

:class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolver\\VariadicValueResolver`
Verifies in the request if your data is an array and will add all of them to the argument list.
When the action is called, the last (variadic) argument will contain all the values of this array.
Verifies in the request if your data is an array and will add all of
them to the argument list. When the action is called, the last (variadic)
argument will contain all the values of this array.

.. note::

Prior to Symfony 3.1, this logic was resolved within the ``ControllerResolver``. The old
functionality is rewritten to the aforementioned value resolvers.
Prior to Symfony 3.1, this logic was resolved within the ``ControllerResolver``.
The old functionality is rewritten to the aforementioned value resolvers.

Adding a Custom Value Resolver
------------------------------

Adding a new value resolver requires one class and one service defintion. In the next example,
you'll create a value resolver to inject the ``User`` object from the security system. Given
you write the following action::
Adding a new value resolver requires one class and one service defintion.
In the next example, you'll create a value resolver to inject the ``User``
object from the security system. Given you write the following action::

namespace AppBundle\Controller;

Expand All @@ -55,8 +59,8 @@ you write the following action::
}
}

Somehow you will have to get the ``User`` object and inject it into the controller. This can be done
by implementing the :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`.
Somehow you will have to get the ``User`` object and inject it into the controller.
This can be done by implementing the :class:`Symfony\\Component\\HttpKernel\\Controller\\ArgumentValueResolverInterface`.
This interface specifies that you have to implement two methods::

interface ArgumentValueResolverInterface
Expand All @@ -74,19 +78,21 @@ This interface specifies that you have to implement two methods::

Both methods get the ``Request`` object, which is the current request, and an
:class:`Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadata`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add "instance" in this sentence: "Both methods get [...] and an ArgumentMetadata instance"

This object contains all information retrieved from the method signature for the
current argument.
This object contains all information retrieved from the method signature for
the current argument.

.. note::

The ``ArgumentMetadata`` is a simple data container created by the
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be an empty line between the directive start (.. note::) and the body (except from the versionadded directive)

:class:`Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory`. This
factory will work on every supported PHP version but might give different results. E.g. the
``isVariadic()`` will never return true on PHP 5.5 and only on PHP 7.0 and higher it will give
you basic types when calling ``getType()``.
:class:`Symfony\\Component\\HttpKernel\\ControllerMetadata\\ArgumentMetadataFactory`.
This factory will work on every supported PHP version but might give
different results. E.g. the ``isVariadic()`` will never return true on
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] isVariadic() method will [...]

PHP 5.5 and only on PHP 7.0 and higher it will give you basic types when
calling ``getType()``.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we really need the whole note here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The information is more of an in-depth description of how it works, I'll remove it for now as this is not important for the reader.


Now that you know what to do, you can implement this interface. To get the current ``User``, you need
the current security token. This token can be retrieved from the token storage.::
Now that you know what to do, you can implement this interface. To get the
current ``User``, you need the current security token. This token can be
retrieved from the token storage::

namespace AppBundle\ArgumentValueResolver;

Expand Down Expand Up @@ -124,33 +130,37 @@ the current security token. This token can be retrieved from the token storage.:
}
}

In order to get the actual ``User`` object in your argument, the given value should fulfill the
following requirements:
In order to get the actual ``User`` object in your argument, the given value
should fulfill the following requirements:

* The argument type (of the method signature) must be typehinted as ``User``;
* The security token must be present;
* The value should be an instance of the ``User``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can remove the "the" :)


When all those requirements are met and true is returned, the ``ArgumentResolver`` calls
``resolve()`` with the same values as it called ``supports()``.
When all those requirements are met and true is returned, the ``ArgumentResolver``
calls ``resolve()`` with the same values as it called ``supports()``.

.. tip::

You can leverage the ``DefaultValueResolver`` by making your resolver accept only mandatory
arguments. Given your signature is `User $user = null`, the above example will not hit ``resolve()``
as one of the conditions does not match. Eventually when the ``DefaultValueResolver`` is asked to
resolve this, it will simply add the default value from the method signature, which results in ``null``.
You can leverage the ``DefaultValueResolver`` by making your resolver
accept only mandatory arguments. Given your signature is `User $user = null`,
the above example will not hit ``resolve()`` as one of the conditions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand what condition is not matched. Do you mean that the resolving is done by DefaultValueResolver because null is not an instance of User or because the argument is not mandatory? The second case is not explicit in the conditions list.

Your example in the docs above uses a mandatory signature of the user, so in fact I don't understand where is the leverage. In that case the default value resolver is not called, but if the token storage returns no user, the user resolver is not called either (what should end with a php error for a missing parameter?).

Actually you user resolver cannot return null since you test it in supports(), so I wonder what's happening if the argument is defaulted, you say here that the default value resolver is called instead of the user resolver, but maybe the last test of supports() is not needed to let the token storage return null but then it should be tested in resolves() by $argument->isOptional() and throw a proper exception when the user cannot be retrieved whereas it is mandatory?

I feel like I miss something...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will rewrite this part to include a clear example

does not match. Eventually when the ``DefaultValueResolver`` is asked
to resolve this, it will simply add the default value from the method
signature, which results in ``null``.

That's it! Now all you have to do is add the configuration for the service container. This
can be done by tagging the service with ``kernel.argument_resolver`` and adding a priority.
That's it! Now all you have to do is add the configuration for the service
container. This can be done by tagging the service with ``kernel.argument_resolver``
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

controller.argument_value_resolver

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a quick check on this string, I seem to have mistaken it more often, but found 0 other usages luckily

and adding a priority.

.. note::

While adding a priority is optional, it's recommended to add one to make sure the expected
value is injected. The ``ArgumentFromAttributeResolver`` has a priority of 100. As this
one is responsible for fetching attributes from the ``Request``, it's also recommended to
trigger your custom value resolver with a lower priority. This makes sure the argument
resolvers are not triggered in (e.g.) subrequests if you pass your user along:
While adding a priority is optional, it's recommended to add one to
make sure the expected value is injected. The ``ArgumentFromAttributeResolver``
has a priority of 100. As this one is responsible for fetching attributes
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 spaces between As this and one is ....

from the ``Request``, it's also recommended to trigger your custom value
resolver with a lower priority. This makes sure the argument resolvers
are not triggered in (e.g.) subrequests if you pass your user along:
``{{ render(controller('AppBundle:User:index', {'user', app.user})) }}``.

.. configuration-block::
Expand Down
0