The routing system does two things:
- It interprets the external URL of incoming requests and transforms it into an internal URI, to determine the module/action and the request parameters.
- It formats the internal URIs used in links into external URLs (provided that you use the link helpers).
The conversion is based on a set of routing rules . These rules are stored in a routing.yml
configuration file located in the application config/
directory. Listing 9-15 shows the default routing rules, bundled with every symfony project.
Listing 9-15 - The Default Routing Rules, in myapp/config/routing.yml
# default rules
homepage:
url: /
param: { module: default, action: index }
default_symfony:
url: /symfony/:action/*
param: { module: default }
default_index:
url: /:module
param: { action: index }
default:
url: /:module/:action/*
9.4.1. Rules and Patterns
Routing rules are bijective associations between an external URL and an internal URI. A typical rule is made up of the following:
- A unique label, which is there for legibility and speed, and can be used by the link helpers
- A pattern to be matched (
url
key) - An array of request parameter values (
param
key)
Patterns can contain wildcards (represented by an asterisk, *
) and named wildcards (starting with a colon, :
). A match to a named wildcard becomes a request parameter value. For instance, the default
rule defined in Listing 9-15 will match any URL like /foo/bar
, and set the module
parameter to foo
and the action
parameter to bar
. And in the default_symfony
rule, symfony
is a keyword and action
is named wildcard parameter.
The routing system parses the routing.yml
file from the top to the bottom and stops at the first match. This is why you must add your own rules on top of the default ones. For instance, the URL /foo/123
matches both of the rules defined in Listing 9-16, but symfony first tests my_rule:
, and as that rule matches, it doesn't even test the default:
one. The request is handled by the mymodule/myaction
action with bar
set to 123
(and not by the foo/123
action).
Listing 9-16 - Rules Are Parsed Top to Bottom
my_rule:
url: /foo/:bar
param: { module: mymodule, action: myaction }
# default rules
default:
url: /:module/:action/*
Note When a new action is created, it does not imply that you must create a routing rule for it. If the default module/action pattern suits you, then forget about the routing.yml
file. If, however, you want to customize the action's external URL, add a new rule above the default one.
Listing 9-17 shows the process of changing the external URL format for an article/read action.
Listing 9-17 - Changing the External URL Format for an article/read
Action
<?php echo url_for('article/read?id=123') ?>
=> /article/read/id/123 // Default formatting
// To change it to /article/123, add a new rule at the beginning
// of your routing.yml
article_by_id:
url: /article/:id
param: { module: article, action: read }
The problem is that the article_by_id
rule in Listing 9-17 breaks the default routing for all the other actions of the article
module. In fact, a URL like article/delete
will match this rule instead of the default
one, and call the read
action with id
set to delete
instead of the delete
action. To get around this difficulty, you must add a pattern constraint so that the article_by_id
rule matches only URLs where the id
wildcard is an integer.
9.4.2. Pattern Constraints
When a URL can match more than one rule, you must refine the rules by adding constraints, or requirements, to the pattern. A requirement is a set of regular expressions that must be matched by the wildcards for the rule to match.
For instance, to modify the article_by_id
rule so that it matches only URLs where the id
parameter is an integer, add a line to the rule, as shown in Listing 9-18.
Listing 9-18 - Adding a Requirement to a Routing Rule
article_by_id:
url: /article/:id
param: { module: article, action: read }
requirements: { id: \d+ }
Now an article/delete
URL can't match the article_by_id
rule anymore, because the 'delete'
string doesn't satisfy the requirements. Therefore, the routing system will keep on looking for a match in the following rules and finally find the default
rule.
9.4.3. Setting Default Values
You can give named wildcards a default value to make a rule work, even if the parameter is not defined. Set default values in the param:
array.
For instance, the article_by_id
rule doesn't match if the id
parameter is not set. You can force it, as shown in Listing 9-19.
Listing 9-19 - Setting a Default Value for a Wildcard
article_by_id:
url: /article/:id
param: { module: article, action: read, id: 1 }
The default parameters don't need to be wildcards found in the pattern. In Listing 9-20, the display
parameter takes the value true
, even if it is not present in the URL.
Listing 9-20 - Setting a Default Value for a Request Parameter
article_by_id:
url: /article/:id
param: { module: article, action: read, id: 1, display: true }
If you look carefully, you can see that article
and read
are also default values for module
and action
variables not found in the pattern.
Tip You can define a default parameter for all the routing rules by defining the sf_routing_default
configuration parameter. For instance, if you want all the rules to have a theme
parameter set to default
by default, add the line sfConfig::set('sf_routing_defaults', array('theme' => 'default'));
to your application's config.php
.
9.4.4. Speeding Up Routing by Using the Rule Name
The link helpers accept a rule label instead of a module/action pair if the rule label is preceded by an at sign (@
), as shown in Listing 9-21.
Listing 9-21 - Using the Rule Label Instead of the Module/Action
<?php echo link_to('my article', 'article/read?id='.$article->getId()) ?>
// can also be written as
<?php echo link_to('my article', '@article_by_id?id='.$article->getId()) ?>
There are pros and cons to this trick. The advantages are as follows:
- The formatting of internal URIs is done much faster, since symfony doesn't have to browse all the rules to find the one that matches the link. In a page with a great number of routed hyperlinks, the boost will be noticeable if you use rule labels instead of module/action pairs.
- Using the rule label helps to abstract the logic behind an action. If you decide to change an action name but keep the URL, a simple change in the
routing.yml
file will suffice. All of thelink_to()
calls will still work without further change. - The logic of the call is more apparent with a rule name. Even if your modules and actions have explicit names, it is often better to call
@display_article_by_slug
thanarticle/display
.
On the other hand, a disadvantage is that adding new hyperlinks becomes less self-evident, since you always need to refer to the routing.yml
file to find out which label is to be used for an action.
The best choice depends on the project. In the long run, it's up to you.
Tip During your tests (in the dev
environment), if you want to check which rule was matched for a given request in your browser, develop the "logs and msgs" section of the web debug toolbar and look for a line specifying "matched route XXX". You will find more information about the web debug mode in Chapter 16.
9.4.5. Adding an .html Extension
Compare these two URLs:
http://myapp.example.com/article/Finance_in_France http://myapp.example.com/article/Finance_in_France.html
Even if it is the same page, users (and robots) may see it differently because of the URL. The second URL evokes a deep and well-organized web directory of static pages, which is exactly the kind of websites that search engines know how to index.
To add a suffix to every external URL generated by the routing system, change the suffix
value in the application settings.yml
, as shown in Listing 9-22.
Listing 9-22 - Setting a Suffix for All URLs, in myapp/config/settings.yml
prod:
.settings:
suffix: .html
The default suffix is set to a period (.
), which means that the routing system doesn't add a suffix unless you specify it.
It is sometimes necessary to specify a suffix for a unique routing rule. In that case, write the suffix directly in the related url:
line of the routing.yml
file, as shown in Listing 9-23. Then the global suffix will be ignored.
Listing 9-23 - Setting a Suffix for One URL, in myapp/config/routing.yml
article_list:
url: /latest_articles
param: { module: article, action: list }
article_list_feed:
url: /latest_articles.rss
param: { module: article, action: list, type: feed }
9.4.6. Creating Rules Without routing.yml
As is true of most of the configuration files, the routing.yml
is a solution to define routing rules, but not the only one. You can define rules in PHP, either in the application config.php
file or in the front controller script, but before the call to dispatch()
, because this method determines the action to execute according to the present routing rules. Defining rules in PHP authorizes you to create dynamic rules, depending on configuration or other parameters.
The object that handles the routing rules is the sfRouting
singleton. It is available from every part of the code by requiring sfRouting::getInstance()
. Its prependRoute()
method adds a new rule on top of the existing ones defined in routing.yml
. It expects four parameters, which are the same as the parameters needed to define a rule: a route label, a pattern, an associative array of default values, and another associative array for requirements. For instance, the routing.yml rule definition shown in Listing 9-18 is equivalent to the PHP code shown in Listing 9-24.
Listing 9-24 - Defining a Rule in PHP
sfRouting::getInstance()->prependRoute(
'article_by_id', // Route name
'/article/:id', // Route pattern
array('module' => 'article', 'action' => 'read'), // Default values
array('id' => '\d+'), // Requirements
);
The sfRouting singleton has other useful methods for handling routes by hand: clearRoutes()
, hasRoutes()
, getRoutesByName()
, and so on. Refer to the API documentation (symfony-project.org/api/1_0/) to learn more.
Tip Once you start to fully understand the concepts presented in this book, you can increase your understanding of the framework by browsing the online API documentation or, even better, the symfony source. Not all the tweaks and parameters of symfony can be described in this book. The online documentation, however, is limitless.