305 lines
9.7 KiB
ReStructuredText
305 lines
9.7 KiB
ReStructuredText
@ParamConverter
|
|
===============
|
|
|
|
Usage
|
|
-----
|
|
|
|
The ``@ParamConverter`` annotation calls *converters* to convert request
|
|
parameters to objects. These objects are stored as request attributes and so
|
|
they can be injected as controller method arguments::
|
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
|
|
|
/**
|
|
* @Route("/blog/{id}")
|
|
* @ParamConverter("post", class="SensioBlogBundle:Post")
|
|
*/
|
|
public function showAction(Post $post)
|
|
{
|
|
}
|
|
|
|
Several things happen under the hood:
|
|
|
|
* The converter tries to get a ``SensioBlogBundle:Post`` object from the
|
|
request attributes (request attributes comes from route placeholders -- here
|
|
``id``);
|
|
|
|
* If no ``Post`` object is found, a ``404`` Response is generated;
|
|
|
|
* If a ``Post`` object is found, a new ``post`` request attribute is defined
|
|
(accessible via ``$request->attributes->get('post')``);
|
|
|
|
* As for other request attributes, it is automatically injected in the
|
|
controller when present in the method signature.
|
|
|
|
If you use type hinting as in the example above, you can even omit the
|
|
``@ParamConverter`` annotation altogether::
|
|
|
|
// automatic with method signature
|
|
public function showAction(Post $post)
|
|
{
|
|
}
|
|
|
|
.. tip::
|
|
|
|
You can disable the auto-conversion of type-hinted method arguments feature
|
|
by setting the ``auto_convert`` flag to ``false``:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
sensio_framework_extra:
|
|
request:
|
|
converters: true
|
|
auto_convert: false
|
|
|
|
.. code-block:: xml
|
|
|
|
<sensio-framework-extra:config>
|
|
<request converters="true" auto-convert="true" />
|
|
</sensio-framework-extra:config>
|
|
|
|
To detect which converter is run on a parameter the following process is run:
|
|
|
|
* If an explicit converter choice was made with
|
|
``@ParamConverter(converter="name")`` the converter with the given name is
|
|
chosen.
|
|
|
|
* Otherwise all registered parameter converters are iterated by priority. The
|
|
``supports()`` method is invoked to check if a param converter can convert
|
|
the request into the required parameter. If it returns ``true`` the param
|
|
converter is invoked.
|
|
|
|
Built-in Converters
|
|
-------------------
|
|
|
|
The bundle has two built-in converters, the Doctrine one and a DateTime
|
|
converter.
|
|
|
|
Doctrine Converter
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
Converter Name: ``doctrine.orm``
|
|
|
|
The Doctrine Converter attempts to convert request attributes to Doctrine
|
|
entities fetched from the database. Two different approaches are possible:
|
|
|
|
- Fetch object by primary key.
|
|
- Fetch object by one or several fields which contain unique values in the
|
|
database.
|
|
|
|
The following algorithm determines which operation will be performed.
|
|
|
|
- If an ``{id}`` parameter is present in the route, find object by primary key.
|
|
- If an option ``'id'`` is configured and matches route parameters, find object by primary key.
|
|
- If the previous rules do not apply, attempt to find one entity by matching
|
|
route parameters to entity fields. You can control this process by
|
|
configuring ``exclude`` parameters or a attribute to field name ``mapping``.
|
|
|
|
By default, the Doctrine converter uses the *default* entity manager. This can
|
|
be configured with the ``entity_manager`` option::
|
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
|
|
|
/**
|
|
* @Route("/blog/{id}")
|
|
* @ParamConverter("post", class="SensioBlogBundle:Post", options={"entity_manager" = "foo"})
|
|
*/
|
|
public function showAction(Post $post)
|
|
{
|
|
}
|
|
|
|
If the placeholder does not have the same name as the primary key, pass the ``id``
|
|
option::
|
|
|
|
/**
|
|
* @Route("/blog/{post_id}")
|
|
* @ParamConverter("post", class="SensioBlogBundle:Post", options={"id" = "post_id"})
|
|
*/
|
|
public function showAction(Post $post)
|
|
{
|
|
}
|
|
|
|
.. tip::
|
|
|
|
The ``id`` option specifies which placeholder from the route gets passed to the repository
|
|
method used. If no repository method is specified, ``find()`` is used by default.
|
|
|
|
This also allows you to have multiple converters in one action::
|
|
|
|
/**
|
|
* @Route("/blog/{id}/comments/{comment_id}")
|
|
* @ParamConverter("comment", class="SensioBlogBundle:Comment", options={"id" = "comment_id"})
|
|
*/
|
|
public function showAction(Post $post, Comment $comment)
|
|
{
|
|
}
|
|
|
|
In the example above, the ``$post`` parameter is handled automatically, but ``$comment`` is
|
|
configured with the annotation since they can not both follow the default convention.
|
|
|
|
If you want to match an entity using multiple fields use the ``mapping`` hash
|
|
option: the key is route placeholder name and the value is the Doctrine
|
|
field name::
|
|
|
|
/**
|
|
* @Route("/blog/{date}/{slug}/comments/{comment_slug}")
|
|
* @ParamConverter("post", options={"mapping": {"date": "date", "slug": "slug"}})
|
|
* @ParamConverter("comment", options={"mapping": {"comment_slug": "slug"}})
|
|
*/
|
|
public function showAction(Post $post, Comment $comment)
|
|
{
|
|
}
|
|
|
|
If you are matching an entity using several fields, but you want to exclude a
|
|
route parameter from being part of the criteria::
|
|
|
|
/**
|
|
* @Route("/blog/{date}/{slug}")
|
|
* @ParamConverter("post", options={"exclude": {"date"}})
|
|
*/
|
|
public function showAction(Post $post, \DateTime $date)
|
|
{
|
|
}
|
|
|
|
If you want to specify the repository method to use to find the entity (for example,
|
|
to add joins to the query), you can add the ``repository_method`` option::
|
|
|
|
/**
|
|
* @Route("/blog/{id}")
|
|
* @ParamConverter("post", class="SensioBlogBundle:Post", options={"repository_method" = "findWithJoins"})
|
|
*/
|
|
public function showAction(Post $post)
|
|
{
|
|
}
|
|
|
|
The specified repository method will be called with the criteria in an ``array``
|
|
as parameter. This is a good fit with Doctrine's ``findBy`` and ``findOneBy``
|
|
methods.
|
|
|
|
There are cases where you want to you use your own repository method and you
|
|
want to map the criteria to the method signature. This is possible when you set
|
|
the ``map_method_signature`` option to true. The default is false::
|
|
|
|
/**
|
|
* @Route("/user/{first_name}/{last_name}")
|
|
* @ParamConverter("user", class="AcmeBlogBundle:User", options={
|
|
* "repository_method" = "findByFullName",
|
|
* "mapping": {"first_name": "firstName", "last_name": "lastName"},
|
|
* "map_method_signature" = true
|
|
* })
|
|
*/
|
|
public function showAction(User $user)
|
|
{
|
|
}
|
|
|
|
class UserRepository
|
|
{
|
|
public function findByFullName($firstName, $lastName)
|
|
{
|
|
...
|
|
}
|
|
}
|
|
|
|
.. tip::
|
|
|
|
When ``map_method_signature`` is ``true``, the ``firstName`` and
|
|
``lastName`` parameters do not have to be Doctrine fields.
|
|
|
|
DateTime Converter
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
Converter Name: ``datetime``
|
|
|
|
The datetime converter converts any route or request attribute into a datetime
|
|
instance::
|
|
|
|
/**
|
|
* @Route("/blog/archive/{start}/{end}")
|
|
*/
|
|
public function archiveAction(\DateTime $start, \DateTime $end)
|
|
{
|
|
}
|
|
|
|
By default any date format that can be parsed by the ``DateTime`` constructor
|
|
is accepted. You can be stricter with input given through the options::
|
|
|
|
/**
|
|
* @Route("/blog/archive/{start}/{end}")
|
|
* @ParamConverter("start", options={"format": "Y-m-d"})
|
|
* @ParamConverter("end", options={"format": "Y-m-d"})
|
|
*/
|
|
public function archiveAction(\DateTime $start, \DateTime $end)
|
|
{
|
|
}
|
|
|
|
Creating a Converter
|
|
--------------------
|
|
|
|
All converters must implement the ``ParamConverterInterface``::
|
|
|
|
namespace Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter;
|
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
|
|
use Symfony\Component\HttpFoundation\Request;
|
|
|
|
interface ParamConverterInterface
|
|
{
|
|
function apply(Request $request, ParamConverter $configuration);
|
|
|
|
function supports(ParamConverter $configuration);
|
|
}
|
|
|
|
The ``supports()`` method must return ``true`` when it is able to convert the
|
|
given configuration (a ``ParamConverter`` instance).
|
|
|
|
The ``ParamConverter`` instance has three pieces of information about the annotation:
|
|
|
|
* ``name``: The attribute name;
|
|
* ``class``: The attribute class name (can be any string representing a class
|
|
name);
|
|
* ``options``: An array of options.
|
|
|
|
The ``apply()`` method is called whenever a configuration is supported. Based
|
|
on the request attributes, it should set an attribute named
|
|
``$configuration->getName()``, which stores an object of class
|
|
``$configuration->getClass()``.
|
|
|
|
To register your converter service, you must add a tag to your service:
|
|
|
|
.. configuration-block::
|
|
|
|
.. code-block:: yaml
|
|
|
|
# app/config/config.yml
|
|
services:
|
|
my_converter:
|
|
class: MyBundle\Request\ParamConverter\MyConverter
|
|
tags:
|
|
- { name: request.param_converter, priority: -2, converter: my_converter }
|
|
|
|
.. code-block:: xml
|
|
|
|
<service id="my_converter" class="MyBundle\Request\ParamConverter\MyConverter">
|
|
<tag name="request.param_converter" priority="-2" converter="my_converter" />
|
|
</service>
|
|
|
|
You can register a converter by priority, by name (attribute "converter"), or
|
|
both. If you don't specify a priority or a name, the converter will be added to
|
|
the converter stack with a priority of ``0``. To explicitly disable the
|
|
registration by priority you have to set ``priority="false"`` in your tag
|
|
definition.
|
|
|
|
.. tip::
|
|
|
|
If you would like to inject services or additional arguments into a custom
|
|
param converter, the priority shouldn't be higher than ``1``. Otherwise, the
|
|
service wouldn't be loaded.
|
|
|
|
.. tip::
|
|
|
|
Use the ``DoctrineParamConverter`` class as a template for your own converters.
|