Relational RESTful controllers routes
=====================================

Sometimes it's better to place subresource actions in their own controller,
especially when you have more than 2 subresource actions.

Resource collection
-------------------

In this case, you must first specify resource relations in special rest YML or
XML collection:

.. code-block:: yaml

    # src/Acme/HelloBundle/Resources/config/users_routes.yml
    users:
        type:     rest
        resource: Acme\HelloBundle\Controller\UsersController

    comments:
        type:     rest
        parent:   users
        resource: Acme\HelloBundle\Controller\CommentsController

.. code-block:: xml

    <!-- src/Acme/HelloBundle/Resources/config/users_routes.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>

    <routes xmlns="http://friendsofsymfony.github.com/schema/rest"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://friendsofsymfony.github.com/schema/rest https://raw.github.com/FriendsOfSymfony/FOSRestBundle/master/Resources/config/schema/routing/rest_routing-1.0.xsd">

        <import id="users" type="rest" resource="Acme\HelloBundle\Controller\UsersController" />
        <import type="rest" parent="users" resource="Acme\HelloBundle\Controller\CommentsController" />
    </routes>

Notice ``parent: users`` option in the second case. This option specifies that
the comments resource is child of the users resource.

It is also necessary to add ``type: rest`` to the ``routing.yml`` file:

.. code-block:: yaml

    # app/config/routing.yml
    acme_hello:
        type: rest
        resource: "@AcmeHelloBundle/Resources/config/users_routes.yml"

In this case, your ``UsersController`` MUST always have a single resource
``get...`` action:

.. code-block:: php

    <?php
    class UsersController extends Controller
    {
        public function getUserAction($slug)
        {} // "get_user"   [GET] /users/{slug}

        // ...
    }

It's used to determine the parent collection name. Controller name itself is not
used in routes auto-generation process and can be any name you like.

Define child resource controller
--------------------------------

``CommentsController`` actions now will looks like:

.. code-block:: php

    <?php
    class CommentsController extends Controller
    {
        public function postCommentVoteAction($slug, $id)
        {} // "post_user_comment_vote" [POST] /users/{slug}/comments/{id}/vote

        public function getCommentsAction($slug)
        {} // "get_user_comments"   [GET] /users/{slug}/comments

        public function getCommentAction($slug, $id)
        {} // "get_user_comment"    [GET] /users/{slug}/comments/{id}

        public function deleteCommentAction($slug, $id)
        {} // "delete_user_comment" [DELETE] /users/{slug}/comments/{id}

        public function newCommentsAction($slug)
        {} // "new_user_comments"   [GET] /users/{slug}/comments/new

        public function editCommentAction($slug, $id)
        {} // "edit_user_comment"   [GET] /users/{slug}/comments/{id}/edit

        public function removeCommentAction($slug, $id)
        {} // "remove_user_comment" [GET] /users/{slug}/comments/{id}/remove
    }

Notice, we got rid of the ``User`` part in action names. That is because the
RestBundle routing already knows, that ``CommentsController::...`` is child
resources of ``UsersController::getUser()`` resource.

Include resource collections in application routing
---------------------------------------------------

Last step is mapping of your collection routes into the application ``routing.yml``:

.. code-block:: yaml

    # app/config/routing.yml
    users:
        type:     rest
        resource: "@AcmeHelloBundle/Resources/config/users_routes.yml"

That's all. Note that it's important to use the ``type: rest`` param when
including your application's routing file. Without it, rest routes will still
work but resource collections will fail. If you get an exception that contains
*"routing loader does not support given key: ``parent``"*, then you are most
likely missing the ``type: rest`` param in your application level routes
include.

Routes naming
-------------

RestBundle uses REST paths to generate route name. This means, that URL:

.. code-block:: text

    [POST] /users/{slug}/comments/{id}/vote

will become the route with the name ``post_user_comment_vote``.

For further examples, see comments of controllers in the code above.

Naming collisions
~~~~~~~~~~~~~~~~~

Sometimes, routes auto-naming will lead to route names collisions, so RestBundle
route collections provides a ``name_prefix`` (``name-prefix`` for xml and
``@NamePrefix`` for annotations) parameter (you can use ``name_prefix`` only in
a file loaded by the rest loader.):

.. code-block:: yaml

    # app/config/routing.yml
    users:
        type: rest  # Required for ``RestYamlLoader`` to process imported routes
        prefix: /api
        resource: "@AcmeHelloBundle/Resources/config/users_routes.yml"

.. code-block:: yaml

    # src/Acme/HelloBundle/Resources/config/users_routes.yml
    comments:
        type:         rest
        resource:     "@AcmeHelloBundle\Controller\CommentsController"
        name_prefix:  api_ # Our precious parameter

With this configuration, route name would become:

.. code-block:: text

    api_vote_user_comment

Say NO to name collisions!

That was it!
