Getting Started with Symfony2 Route Annotations

Share this article

When you download the Standard Symfony 2 Distribution, it includes an interesting bundle named SensioFrameworkExtraBundle which implements a lot of great stuff, especially the opportunity to use annotations directly within your controllers.

The idea behind this article is not to convice developers to embrace this way of doing, but to point the finger at an alternative method to easily configure controllers. Keep in mind that there is no magic recipe, it depends on what you need in each specific scenario.

Symfony 2 implements a strong built-in component to manage all the routes of an application: the Routing Component. Basically, a route maps a URL to a controller action. Because Symfony is intended to be modular, a file is dedicated to this: routing.yml. You will find it in app > config > routing.yml.

In order to understand how to define our routes with annotations, I’ll take as example a simple blog application.

Step 1: creating the homepage route

We just want to link the path / to an action of our HomeController.

Without annotations

In app/config/routing.yml:

blog_front_homepage:
  path : /
  defaults:  { _controller: BlogFrontBundle:Home:index }

In src/Blog/FrontBundle/Controller/HomeController.php:

<?php
namespace Blog\FrontBundle\Controller;

class HomeController
{
    public function indexAction()
    {
        //... create and return a Response object
    } 
}

Let’s take a look at those files. In routing.yml, we declared a simple configuration for the route named blog_front_homepage with 2 parameters: the path and the action of the controller we want to target. As far as the controller goes, it doesn’t need anything special.

With annotations

In app/config/routing.yml:

blog_front:
    resource: "@BlogFrontBundle/Controller/"
    type:     annotation
    prefix:   /

In src/Blog/FrontBundle/Controller/HomeController.php:

<?php

namespace Blog\FrontBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class HomeController
{
    /**
     * @Route("/", name="blog_home_index")
     */
    public function indexAction() { /* ... */ }
}

First, let’s see what happened to routing.yml:

  • resource targets the controller to impact
  • type obviously defines the way we declare routes
  • prefix defines a prefix for all actions of a controller class (optional)

What’s more interesting is how our controller is now built. Before everything else, we have to call the relevant class of the SensioFrameworkExtraBundle: use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;. We are now ready to implement the route and the parameters we want to assign: in this case only the path and the name (we will later see all that we can do): @Route("/", name="blog_homepage").

Some of you might be thinking: “Well, we know how to override the controller with the routing layer, so what? At the end of the day, more code is needed for basically the same result”. And you would be right… At least for now.

Step 2: adding an article page route

Without annotations

In app/config/routing.yml:

blog_front_homepage:
  path : /
  defaults:  { _controller: BlogFrontBundle:Home:index }

blog_front_article:
  path : /article/{slug}
  defaults:  { _controller: BlogFrontBundle:Home:showArticle }

In src/Blog/FrontBundle/Controller/HomeController.php:

<?php
// namespace & uses...

class HomeController
{
    public function indexAction() { /* ... */ }
    
    public function showArticleAction($slug) { /* ... */ }
}

With annotations

In app/config/routing.yml:

blog_front:
    resource: "@BlogFrontBundle/Controller/"
    type:     annotation
    prefix:   /

In src/Blog/FrontBundle/Controller/HomeController.php:

<?php
// namespace & uses...

class HomeController
{
    /**
     * @Route("/", name="blog_home_index")
     */
    public function indexAction() { /* ... */ }
    
    /**
     * @Route("/article/{slug}", name="blog_home_show_article")
     */
    public function showArticleAction($slug) { /* ... */ }
}

Have you noticed? routing.yml doesn’t need any changes. Now, you can check from a single glance which action is being called from a route pattern.

If you want all your actions from the controller to have a prefix, for instance /admin, you can remove prefix key from routing.yml and add an extra @Route annotation at the top of your class:

In app/config/routing.yml:

blog_front: ...
 
blog_admin:
    resource: "@BlogAdminBundle/Controller/"
    type:     annotation

In src/Blog/AdminBundle/Controller/AdminController.php:

<?php
// namespace & uses...

/**
 * @Route("/admin")
 */
class AdminController
{
    /**
     * @Route("/", name="blog_admin_index")
     */
    public function indexAction() { /* ... */ }
    
    /**
     * @Route("/create", name="blog_admin_create_article")
     */
    public function createArticleAction() { /* ... */ }
}

Step 3: extra routing configuration

Setting URL default parameters

Syntax: defaults = { "key" = "value" }.

/**
 * @Route(
 *       path     = "/article/{slug}",
 *       name     = "blog_home_show_article",
 *       defaults = { "slug" = "hello" }
 * )
 */

By adding slug to the defaults key, the {slug} placeholder is no longer required. The URL /article will match this route and the value of the slug parameter will be set to hello. The URL /blog/world will also match, giving the page parameter a value of world.

Adding requirements

Syntax: requirements = { "key" = "value" }.

/**
 * @Route(
 *       path         = "/article/{slug}",
 *       name         = "blog_home_show_article",
 *       defaults     = { "slug" = "hello" },
 *       requirements = { "slug" = "[a-z]+" }
 * )
 */

We can define requirements for the slug key with a regular expression, to explicitly define that the value of {slug} has to be exclusively made of alpha characters. In the following example, we do the exact same thing with digits:

/**
 * @Route(
 *       path         = "article/{id}",
 *       name         = "blog_home_show_article",
 *       defaults     = { "id" = 1 },
 *       requirements = { "id" = "\d+" } 
 * )
 */

If you need more information about regular expressions follow this link.

Enforcing the HTTP method

Syntax: methods = { "request method" }.

/**
 * @Route(
 *       path         = "/article/{slug}",
 *       name         = "blog_home_show_article",
 *       defaults     = { "slug" = "hello" },
 *       requirements = { "slug" = "[a-z]+" },
 *       methods      = { "GET", "POST" }
 * )
 */

We can also match on the method of the incoming request (i.e. GET, POST, PUT, DELETE). Remember that if no method is specified, the route will match any of them.

Enforcing the HTTP scheme

Syntax: schemes = { "protocol" }.

/**
 * @Route(
 *       path         = "/article/{slug}",
 *       name         = "blog_home_show_article",
 *       defaults     = { "slug" = "hello" },
 *       requirements = { "slug" = "[a-z]+" },
 *       methods      = { "GET", "POST" },
 *       schemes      = { "https" }  
 * )
 */

In this case, we want to secure the route to be sure that it is accessed via the HTTPS protocol.

Enforcing the host name

Syntax: host = "myhost.com".

/**
 * @Route(
 *       path         = "/article/{slug}",
 *       name         = "blog_home_show_article",
 *       defaults     = { "slug" = "hello" },
 *       requirements = { "slug" = "[a-z]+" },
 *       methods      = { "GET", "POST" },
 *       schemes      = { "https" },
 *       host         = "myblog.com"
 * )
 */

We can also match on the HTTP host. This one will match only if the host is myblog.com.

Step 4: exercise

As we are now able to build a solid routing structure, imagine that we have to create the action to delete an article with the right route in our AdminController.php. We have to:

  • Define the path as /admin/delete/article/{id};
  • Define the name as blog_admin_delete_article;
  • Define the requirement for the key id as only digits;
  • Define the GET request method.

Stuck? Have a look at the solution below:

<?php
// src/Blog/AdminBundle/Controller/AdminController.php
// namespace & uses...

/**
 * @Route("/admin")
 */
class AdminController
{
    /* ... */

    /**
     * @Route("
     *    path          = "/delete/article/{id}", 
     *    name          = "blog_admin_delete_article"
     *    requirements  = { "id" = "\d+" },
     *    methods       = { "GET" }
     * )
     */
    public function deleteArticleAction($id) { /* ... */ }
}

Final thoughts

As you can see, managing routes with annotations is both easy to write, and easy to read and maintain. It also has the nice pro of gathering both the code and the configuration in a unique file: the controller class.

Do you use annotations, or the standard configuration? Which do you prefer, and why?

Frequently Asked Questions (FAQs) about Symfony2 Route Annotations

What are Symfony2 Route Annotations and why are they important?

Symfony2 Route Annotations are a way of defining routes directly within the controller action that they correspond to. This is a powerful feature of Symfony2 as it allows developers to keep the route definition and the corresponding controller action in the same place, making the code easier to understand and maintain. They are important because they provide a clear and concise way to define routes, reducing the complexity of the routing configuration.

How do I install and enable annotations in Symfony2?

To use annotations in Symfony2, you need to install the SensioFrameworkExtraBundle. This can be done using Composer, a dependency management tool for PHP. Once the bundle is installed, you need to enable it in your application’s kernel. This is done by adding new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle() to the $bundles array in the AppKernel.php file.

How do I define a route using annotations in Symfony2?

To define a route using annotations in Symfony2, you need to add a comment above the controller action that the route corresponds to. This comment should start with @Route and include the path of the route. For example, @Route(“/hello/{name}”, name=”hello”) would define a route that matches /hello/{name} and maps it to the controller action below the comment.

Can I define multiple routes for a single controller action using annotations?

Yes, you can define multiple routes for a single controller action using annotations. This can be useful if you want to match different URLs to the same action. To do this, simply add multiple @Route comments above the controller action.

How do I define route requirements using annotations in Symfony2?

Route requirements can be defined using annotations by adding a requirements key to the @Route comment. The requirements key should be followed by an array of requirement definitions. For example, @Route(“/hello/{name}”, requirements={“name” = “\d+”}) would define a route that only matches if the {name} parameter is a number.

How do I define default values for route parameters using annotations in Symfony2?

Default values for route parameters can be defined using annotations by adding a defaults key to the @Route comment. The defaults key should be followed by an array of default value definitions. For example, @Route(“/hello/{name}”, defaults={“name” = “World”}) would define a route that uses “World” as the default value for the {name} parameter if it is not provided in the URL.

Can I use annotations to define routes in YAML or XML files?

No, annotations can only be used to define routes directly within PHP files. If you want to define routes in YAML or XML files, you need to use the traditional routing configuration methods provided by Symfony2.

How do I debug routes defined using annotations in Symfony2?

You can debug routes defined using annotations in Symfony2 using the console command debug:router. This command will display a list of all routes defined in your application, including those defined using annotations.

Can I use annotations to define routes in Symfony2 without the SensioFrameworkExtraBundle?

No, the SensioFrameworkExtraBundle is required to use annotations to define routes in Symfony2. This bundle provides the annotation reader that Symfony2 uses to parse the @Route comments.

Are there any limitations or drawbacks to using annotations to define routes in Symfony2?

One potential drawback to using annotations to define routes is that it can make your controller actions more complex, as they now also contain routing configuration. However, this can be mitigated by keeping your routes simple and well-documented. Another potential limitation is that annotations can only be used in PHP files, not in YAML or XML files.

Nicolas ScolariNicolas Scolari
View Author

Nicolas Scolari is a back-end developer from France specialized in PHP and currently actively experimenting with Symfony 2. He also loves fiddling with front-end technologies, trying to mix both sides in order to become a better guy.

annotationsBrunoSbundleframeworkmvcOOPHPPHProutesroutingsymfonysymfony2
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week