The definitive guide of Symfony 1.0

9.1. What Is Routing?

Routing is a mechanism that rewrites URLs to make them more user-friendly. But to understand why this is important, you must first take a few minutes to think about URLs.

9.1.1. URLs As Server Instructions

URLs carry information from the browser to the server required to enact an action as desired by the user. For instance, a traditional URL contains the file path to a script and some parameters necessary to complete the request, as in this example:

This URL conveys information about the application's architecture and database. Developers usually hide the application's infrastructure in the interface (for instance, they choose page titles like "Personal profile page" rather than "QZ7.65"). Revealing vital clues to the internals of the application in the URL contradicts this effort and has serious drawbacks:

  • The technical data appearing in the URL creates potential security breaches. In the preceding example, what happens if an ill-disposed user changes the value of the id parameter? Does this mean the application offers a direct interface to the database? Or what if the user tries other script names, like admin.php, just for fun? All in all, raw URLs offer an easy way to hack an application, and managing security is almost impossible with them.
  • The unintelligibility of URLs makes them disturbing wherever they appear, and they dilute the impact of the surrounding content. And nowadays, URLs don't appear only in the address bar. They appear when a user hovers the mouse over a link, as well as in search results. When users look for information, you want to give them easily understandable clues regarding what they found, rather than a confusing URL such as the one shown in Figure 9-1.
URLs appear in many places, such as in search results

Figure 9.1 URLs appear in many places, such as in search results

  • If one URL has to be changed (for instance, if a script name or one of its parameters is modified), every link to this URL must be changed as well. It means that modifications in the controller structure are heavyweight and expensive, which is not ideal in agile development.

And it could be much worse if symfony didn't use the front controller paradigm; that is, if the application contained many scripts accessible from the Internet, in many directories, such as these:

In this case, developers would need to match the URL structure with the file structure, resulting in a maintenance nightmare when either structure changed.

9.1.2. URLs As Part of the Interface

The idea behind routing is to consider the URL as part of the interface. The application can format a URL to bring information to the user, and the user can use the URL to access resources of the application.

This is possible in symfony applications, because the URL presented to the end user is unrelated to the server instruction needed to perform the request. Instead, it is related to the resource requested, and it can be formatted freely. For instance, symfony can understand the following URL and have it display the same page as the first URL shown in this chapter:

The benefits are immense:

  • URLs actually mean something, and they can help the users decide if the page behind a link contains what they expect. A link can contain additional details about the resource it returns. This is particularly useful for search engine results. Additionally, URLs sometimes appear without any mention of the page title (think about when you copy a URL in an e-mail message), and in this case, they must mean something on their own. See Figure 9-2 for an example of a user-friendly URL.
URLs can convey additional information about a page, like the publication date

Figure 9.2 URLs can convey additional information about a page, like the publication date

  • URLs written in paper documents are easier to type and remember. If your company website appears as on your business card, it will probably not receive many visits.
  • The URL can become a command-line tool of its own, to perform actions or retrieve information in an intuitive way. Applications offering such a possibility are faster to use for power users.
// List of results: add a new tag to narrow the list of results

// User profile page: change the name to get another user profile
  • You can change the URL formatting and the action name/parameters independently, with a single modification. It means that you can develop first, and format the URLs afterwards, without totally messing up your application.
  • Even when you reorganize the internals of an application, the URLs can remain the same for the outside world. It makes URLs persistent, which is a must because it allows bookmarking on dynamic pages.
  • Search engines tend to skip dynamic pages (ending with .php, .asp, and so on) when they index websites. So you can format URLs to have search engines think they are browsing static content, even when they meet a dynamic page, thus resulting in better indexing of your application pages.
  • It is safer. Any unrecognized URL will be redirected to a page specified by the developer, and users cannot browse the web root file structure by testing URLs. The actual script name called by the request, as well as its parameters, is hidden.

The correspondence between the URLs presented to the user and the actual script name and request parameters is achieved by a routing system, based on patterns that can be modified through configuration.

Note How about assets? Fortunately, the URLs of assets (images, style sheets, and JavaScript) don't appear much during browsing, so there is no real need for routing for those. In symfony, all assets are located under the web/ directory, and their URL matches their location in the file system. However, you can manage dynamic assets (handled by actions) by using a generated URL inside the asset helper. For instance, to display a dynamically generated image, use image_tag(url_for('captcha/image?key='.$key)).

9.1.3. How It Works

Symfony disconnects the external URL and its internal URI. The correspondence between the two is made by the routing system. To make things easy, symfony uses a syntax for internal URIs very similar to the one of regular URLs. Listing 9-1 shows an example.

Listing 9-1 - External URL and Internal URI

// Internal URI syntax

// Example internal URI, which never appears to the end user

// Example external URL, which appears to the end user

The routing system uses a special configuration file, called routing.yml, in which you can define routing rules. Consider the rule shown in Listing 9-2. It defines a pattern that looks like articles/*/*/* and names the pieces of content matching the wildcards.

Listing 9-2 - A Sample Routing Rule

  url:    articles/:subject/:year/:title.html
  param:  { module: article, action: permalink }

Every request sent to a symfony application is first analyzed by the routing system (which is simple because every request in handled by a single front controller). The routing system looks for a match between the request URL and the patterns defined in the routing rules. If a match is found, the named wildcards become request parameters and are merged with the ones defined in the param: key. See how it works in Listing 9-3.

Listing 9-3 - The Routing System Interprets Incoming Request URLs

// The user types (or clicks on) this external URL

// The front controller sees that it matches the article_by_title rule
// The routing system creates the following request parameters
  'module'  => 'article'
  'action'  => 'permalink'
  'subject' => 'finance'
  'year'    => '2006'
  'title'   => 'activity-breakdown'

Tip The .html extension of the external URL is a simple decoration and is ignored by the routing system. Its sole interest is to makes dynamic pages look like static ones. You will see how to activate this extension in the "Routing Configuration" section later in this chapter.

The request is then passed to the permalink action of the article module, which has all the required information in the request parameters to determine which article is to be shown.

But the mechanism also must work the other way around. For the application to show external URLs in its links, you must provide the routing system with enough data to determine which rule to apply to it. You also must not write hyperlinks directly with <a> tags — this would bypass routing completely — but with a special helper, as shown in Listing 9-4.

Listing 9-4 - The Routing System Formats Outgoing URLs in Templates

// The url_for() helper transforms an internal URI into an external URL
<a href="<?php echo url_for('article/permalink?subject=finance&year=2006&title=activity-breakdown') ?>">click here</a>

// The helper sees that the URI matches the article_by_title rule
// The routing system creates an external URL out of it
 => <a href="">click here</a>

// The link_to() helper directly outputs a hyperlink
// and avoids mixing PHP with HTML
<?php echo link_to(
  'click here',
) ?>

// Internally, link_to() will make a call to url_for() so the result is the same
=> <a href="">click here</a>

So routing is a two-way mechanism, and it works only if you use the link_to() helper to format all your links.