Mugo Web main content.

A primer on Symfony routes and how to use match conditions

By: Carlos Mauri | March 23, 2020 | symfony and routing

When working with Symfony applications, the routing component is key to mapping your website URLs to custom PHP code in controllers. In this post, we explain how to define URL routes and how to use match conditions.

The examples and explanations in this post are based on Symfony 3.4.

What is the Symfony routing component?

Routing is key to building an SEO-friendly URL schema. Your ugly and hard to read URLs such as this:

index.php?id=12&type=post&category=symfony

... will become beautiful SEO-friendly and user-friendly URLs without you having to become an Apache mod_rewrite guru.

apache mod_rewrite guru

The routing component maps URLs to code when Symfony receives requests. In most cases, the code is in a controller action and your controller will generate the response.

Defining a route

You can create a route in 4 different formats: annotations, YAML, XML, and PHP. All 4 methods provide the same features and performance, so you can choose the one that best suits your needs. Here, we will use YAML.

To create a route you need at least 3 things:

  • A name: this is an internal, unique route identifier
  • A path: the URL you want to map
  • A controller: the PHP function containing your code

Here is an example route in the routing.yml file:

contact_us_render:
    path: /contact-us
    defaults:
        _controller: 'MugoBundle:Contact:render'

This basic route maps the URL https://yourdomain.com/contact-us to the render action located in your Contact controller

The renderAction method will generate the response and render the template. For example, inside the ContactController class, you would have code like this:

public function renderAction( Request $request )
{
  // your logic
  [...]
  // render the template
  return $this->render( 
    'path_to_your_template/contact_us.html.twig', 
    $templateVars 
  );
}

Building a dynamic route

Routing also allows you to map dynamic URLs by using parameters. Parameters are defined inside braces {}. Consider this example:

news_show:
    path: /news/{year}/{url}
    defaults: 
        _controller: 'MugoBundle:News:show'

This route will match /news/*/*, but what if you want to match a 4-digit number as the "year" parameter? The "requirements" option is where you can define this:

news_show: 
    path: /news/{year}/{url} 
    defaults:
        _controller: 'MugoBundle:News:show'
    requirements:
        year: '\d{4}'

In this example, "\d{4}" is a regular expression that matches a digit of length 4. Note that in the "requirements" option you must use regular expressions. You can use a tool such as regex101 to test regular expressions.

In your controller, you can access the URL parameter values using the Request object:

public function showAction( Request $request ) 
{ 
  // get URL parameters
  $year = $request->GET( 'year' );
  $url  = $request->GET( 'url' );
  [...]
  // render the template 
  return $this->render( 
    'path_to_your_template/news_entry.html.twig', 
    $templateVars 
  ); 
}

Using conditions to match your route

Using the options shown so far, you can map static and dynamic URLs with parameters, you can limit those parameters using regular expressions, and you can also match any route depending on the HTTP host of the incoming request using the hostname pattern. If you need more powerful match conditions, you need to use the condition setting.

The "condition" option is represented by a valid ExpressionLanguage expression where you can access many Symfony variables. Let's review the following example:

rewards_show_chrome_top:
    path: /rewards
    defaults: 
        _controller: 'MugoBundle:Rewards:showChromeTop'
    condition: "request.headers.get('User-Agent') matches '/chrome/i' and request.query.get('prize') == 'top'"

rewards_show_top:
    path: /rewards
    defaults:
        _controller: 'MugoBundle:Rewards:showTop'
    condition: "request.query.get('prize') == 'top'"

rewards_show:
    path: /rewards
    defaults:
        _controller: 'MugoBundle:Rewards:show'

All of the routes match the same path /rewards. However, the first and second routes each have special match conditions.

The first route has multiple conditions. It looks for the browser's user agent and for the "prize" query parameter. If the user agent contains the word "chrome" and the "prize" query parameter is equal to "top", we have a match. Symfony will load the showChromeTop action located within the Rewards controller.

The second route is less specific and only includes 1 condition. It will match if the "prize" query parameter is equal to "top".

The third route is the most generic as it has no conditions.

When multiple routes match the same URL, the first match is used. Therefore, the order matters. If the third route was placed before the rewards_show_chrome_top and rewards_show_top routes, those other routes would never match.

Debugging a route

At first, your web application will have just a few routes. But as you continue to build your application, eventually it can become harder to debug all of the routes, especially if they are spread over many bundles. Fortunately, Symfony provides a console command where you can list all your routes, or, if you provide the route name, Symfony will show detailed information about your route. 

To display all your available routes, run the following command from the root of your Symfony installation:

php bin/console debug:router

To get more information for a specific route, include the route name after the debug:router action:

php bin/console debug:router contact_us_render

And if you want to find the matched route based on a URL, use the router:match command:

php bin/console router:match /news/2020/my-symfony-site

Conclusion

Symfony routing is a powerful and flexible Symfony component that enables you to fully control your website application's URLs. If you have questions, doubts, or concerns about routing, leave us a comment or get in touch anytime.

Comments

blog comments powered by Disqus