Skip to content
This repository was archived by the owner on Sep 23, 2022. It is now read-only.

Routing

Florian Klein edited this page Sep 6, 2013 · 3 revisions

Conventional routing loader. Current loader has 2 drawbacks:

  1. Extreme verbosity
  2. Names ambiguity
blog_display:
    pattern:      /blogs/{id}
    defaults:     { _controller: App:Blog:show }
    requirements: { id: '\d+' }

This could look like a short record, but in reality, for a simple CRUD you'll need 7 of those, forcing you to repeat yourself with parameters or patterns from controller to controller, from project to project.

Even worse than that - there's more important problem here - naming problem. You have 3 different entity names for each route record:

  1. Route name (blog_display)
  2. Action name (App:Blog:show)
  3. URL pattern (/blogs/{id})

Though they all represent the same URL<->Controller mapping, they are really loosely coupled, which means that you need to maintain them separately during the whole lifetime of the project. Have you ever asked yourself "how to name routes for this controller?" or "how to name this action?". That's the kind of questions Symfony2 forces you to answer from project to project, again and again. Also, that's the kind of questions that could be answered once and for all with some simple convention.

Conventional Routing Loader

So let us present you the first big improvement of RadBundle - Conventional Routing Loader. It provides routing configuration support, which lives somewhere in between RoR and Symfony2, but provides you all the flexibility that the standard routing.yml does.

First thing, Routing Loader is purely based on a couple of conventions. Those conventions are simple rules like:

  • Controller name as objects collection presentation (plural)
  • Most controllers could be represented with 5 crud basic actions:
    • indexAction - list items
    • newAction - show form or save new item
    • showAction - show single item
    • editAction - show form or update existing item
    • deleteAction - delete item from collection

Following this simple convention, your Symfony2 controller will look like:

namespace App\Controller;

class BlogsController
{
    public function indexAction() {}
    public function newAction(Request $request) {}
    public function showAction($id) {}
    public function editAction($id) {}
    public function deleteAction($id) {}
}

If you follow this simple convention, conventional routing loader will be able to do most of the job for you. Only thing you will need to do is to add this line to routing.yml:

App:Blogs: ~

This record will automatically expand into full routing map like that:

route name URL pattern HTTP method controller action
app_blogs_index /blogs GET App\Controller\BlogsController::indexAction()
app_blogs_new /blogs/new GET App\Controller\BlogsController::newAction()
app_blogs_create /blogs POST App\Controller\BlogsController::newAction()
app_blogs_show /blogs/{id} GET App\Controller\BlogsController::showAction($id)
app_blogs_edit /blogs/{id}/edit GET App\Controller\BlogsController::editAction($id)
app_blogs_update /blogs/{id} PUT App\Controller\BlogsController::editAction($id)
app_blogs_delete /blogs/{id} DELETE App\Controller\BlogsController::deleteAction($id)

Ok, but what if instead of /blogs, you want those routes to live inside /admin/blogs URL?

App:Blogs: /admin/blogs

Will generate this route map:

route name URL pattern HTTP method controller action
app_blogs_index /admin/blogs GET App\Controller\BlogsController::indexAction()
app_blogs_new /admin/blogs/new GET App\Controller\BlogsController::newAction()
app_blogs_create /admin/blogs POST App\Controller\BlogsController::newAction()
app_blogs_show /admin/blogs/{id} GET App\Controller\BlogsController::showAction($id)
app_blogs_edit /admin/blogs/{id}/edit GET App\Controller\BlogsController::editAction($id)
app_blogs_update /admin/blogs/{id} PUT App\Controller\BlogsController::editAction($id)
app_blogs_delete /admin/blogs/{id} DELETE App\Controller\BlogsController::deleteAction($id)

What if you don't want a whole CRUD? What if the only thing you need is index and show? You can specify which collections or resource actions you want to map:

App:Blogs:
    collections: [ index ]
    resources: [ show ]

Will generate this routes map:

route name URL pattern HTTP method controller action
app_blogs_index /blogs GET App\Controller\BlogsController::indexAction()
app_blogs_show /blogs/{id} GET App\Controller\BlogsController::showAction($id)

Want custom object action? No problem:

App:Blogs:
    collections: [ index ]
    resources: [ show, ban ]

Will generate this route map:

route name URL pattern HTTP method controller action
app_blogs_index /blogs GET App\Controller\BlogsController::indexAction()
app_blogs_show /blogs/{id} GET App\Controller\BlogsController::showAction($id)
app_blogs_ban /blogs/{id}/ban PUT App\Controller\BlogsController::banAction($id)

Don't like default requirements or pattern for some method? You can always fine-tune it manually:

App:Blogs:
    collections: [ index ]
    resources:
        show: ~
        ban:
            pattern: /ban-this-guy/{id}
            requirements: { _method: 'GET' }

Will generate this route map:

route name URL pattern HTTP method controller action
app_blogs_index /blogs GET App\Controller\BlogsController::indexAction()
app_blogs_show /blogs/{id} GET App\Controller\BlogsController::showAction($id)
app_blogs_ban /blogs/ban-this-guy/{id} GET App\Controller\BlogsController::banAction($id)

You can even add collection-wide requirements to your routes:

App:Blogs:
    collections: [ index ]
    resources:
        requirements: { id: '\d+' }

        show: ~
        ban:
            pattern: /ban-this-guy/{id}
            requirements: { _method: 'GET' }

Will add "id should be number" requirement to all resourceful routes.

Ok, but what if you want a whole CRUD collection but at the same time you need to add couple of custom actions? No need to expand your controller route tree. You could just describe specific actions:

App:Blogs: ~

App:Blogs:ban: /blogs/{id}/ban

App:Blogs:unban: /blogs/{id}/unban

That's it, I think. If you have any questions - ask them on IRC (#geekweek12 [at] freenode.net).

Complete Example

# generating routes for Cheese collection based on conventions
App:Cheeses: ~

# custom pattern
App:Cheeses: /good/cheeses

# a bit expanded to specify needed actions
App:Cheeses:
    resources:  [show, edit]
    collections: [index, new]

# a bit more expanded version of specific routes
App:Cheeses:
    resources:
        show:   /cheeses/{id}

        # full version of the one above
        show:
            pattern:        /cheeses/{id}
            defaults:       ~
            requirements:   ~

        edit:   /cheeses/{id}/edit
        delete: /cheeses/{id}

    collections:
        index:  /cheeses
        new:    /cheeses/new

# single resource action
App:Cheeses:list: /cheeses/{id}/list

Update for Symfony 2.2

In Symfony 2.2, the _method request parameter support has been disabled by default.

To handle, for example, "PUT" or "DELETE" methods, it is necessary to execute this :

Request::enableHttpMethodParameterOverride();

Suggest : it can be done in the boot method of your bundle.

Clone this wiki locally