Development

Documentation/fr_FR/book/trunk/routing

You must first sign up to be able to contribute.

Comment modifier la politique de reecriture =============================

Overview


Un des problemes les plus importants posé par un framework est l'aspect des URLs générés: elles sont souvent longues, complexe et mal réferencable par les moteurs de recherches. Symfony propose un nouveau systeme de *routing* qui vous donnera le controle total sur les adresses de vos applications.

Introduction


Prenons le cas d'une application de type blog ou les utilisateurs publient des articles. Avec symfony, pour afficher cet article, vous ne voulez pas d'une URL comme ca:

http://myapp.example.com/index.php/article/read/id/100

Cette URL appelle l'action read du module article avec pour parametre id : 100

Pour optimiser le travail des moteur de recherche dans l'indexation des pages des sites dynamiques, et rendre les URLs plus lisibles, quelques outils de blog propose une option *permalink*. Un permalink (lien permanent) est une adresse definie et permanente pour les favoris des navigateurs, et les moteurs de recherche. En reprenant l'exemple précedent, le permalink serait:

http://myapp.example.com/index.php/article/permalink/title/my_article_title

La seule difference avec la premiere URL est l'utilisation de mots clefs plus parlant. L'action permalink a pour but de convertir la variable title dans l'id de l'article, en cherchant en base l'adresse d'une page permanente ou rediriger le moteur de recherche.

Cette methode est capable d'afficher des URLs encore plus simple et parlante, par exemple:

http://myapp.example.com/article/my_article_title // ou pourquoi pas http://myapp.example.com/2005/06/25/my_article_title

La facon la plus simple est d'utiliser le module mod_rewrite du serveur Apache, avec l'URL rewriting. L'URL rewriting convertit les URLs en quelquechose comprehensible pour Apache:

1. Apache recoit une requete pour http://myapp.example.com/2005/06/25/my_article_title 2. mod_rewrite convertit cette URL en http://myapp.example.com/index.php/article/read/title/my_article_title 3. Apache sait qu'il doit executer index.php avec /article/read/title/my_article_title comme valeur pour le path_info

Cette solution a deux conditions necessaires:

* vous avez besoin d'un serveur Apache avec le module mod_rewrite * l'URL rewriting est la seule solution

En depit des faits, si vous voulez creer une URL vers cet article, vous aurez besoin de convertir manuellement la base de l'URL en une "smart" (plus parlante) URL. L'URL entré (utilisé par le module mod_rewrite) et l'URL généré (utilisé par l'application) sont completement differente

Symfony peut en natif convertir les URLs générés et interpretés les URLs entrées. Par consequent, vous pouvez creer des associations entre les URLs et les controller. Cette reecriture est appelé *routing* dans symfony, relié a un fichier de configuration nommmé routing.yml que vous pouvez retrouver dans le repertoire config/ de chaque application.

//MIKA

Routing input URLs


### Rules and patterns

The routing.yml contains **rules**, or bijective associations between a URL pattern and the "real" request parameter. A typical rule file contains:

* a label, which is there for legibility and can be used by the [link helpers](templating_link_helpers.txt) * an url key showing the pattern to be matched * a param used to set default values for some of the arguments of the "real" call

Here is an extract of the routing.yml file that illustrates the rewriting of our example blog URL:

article_by_title_with_date:

url: /:year/:month/:day/:title param: { module: article, action: permalink }

The rule stipulates that every request showing the pattern /:year/:month/:day/:title will have to be transformed into a call to the permalink action of the article module with arguments year, month, day and title taken from the base URL.

So the example URL

http://myapp.example.com/index.php/2005/06/25/my_article_title

Will be understood as if written

http://myapp.example.com/index.php/article/permalink/year/2005/month/06/day/25/title/my_article_title

...and call the permalink action of the article module with the following arguments:

[php] $this->getRequestParameter('year') => 2005 $this->getRequestParameter('month') => 06 $this->getRequestParameter('day') => 25 $this->getRequestParameter('title') => my_article_title

Let's add a second rule to handle URLs like:

http://myapp.example.com/index.php/article/100

Simply add the following lines in the routing.yml file:

article_by_id:

url: /article/:id param: { module: article, action: read }

Notice that in the pattern, the word article is a string whereas id is a variable (because it starts with a :).

**Note**: you smart readers may have guessed that as soon as a rule such as the one mentioned above is added, the default rule (which is /:module/:action/*) will not work anymore with the article module, because the module name will match the pattern /article/:id first. If you start creating rules with strings that match the names of your modules, you probably need to change the default rule to something like: default:

url: /action/:module/:action/*

### Pattern constraints

Now what if you needed to have access to articles from their title:

http://myapp.example.com/index.php/article/my_article_title

Well, this looks problematic. This URL should be routed to the permalink action, but it already satisfies the articl_by_id rule and will be automatically routed to the read action. To solve this issue, each entry can take a third parameter called requirements to specify constraints in the pattern (in the shape of a regular expression). That means that you can modify the previous rule to route URLs to read only if the id argument is an integer:

article_by_id:

url: /article/:id param: { module: article, action: read } requirements: { id: \d+$ }

Now you can add a third rule to gain access to articles from their title:

article_by_title:

url: /article/:permalink param: { module: article, action: permalink }

Rules are ordered and the routing engine takes the first one that satisfies the pattern and the pattern constraints. That's why you don't need to add a constraint to the last rule (specifying that permalink can not be an integer):

* /article/100 matches the first rule and will be handed to read * /article/my_article_title doesn't match the first rule but matches the second, so it will be handed to permalink

Now that you know about pattern constraints, that would be a good thing to add some to the very first rule:

article_by_title_with_date:

url: /:year/:month/:day/:title param: { module: article, action: permalink } requirements: { year: \d{4}$, month: \d\d$, day: \d\d$ }

The routing engine allows you to handle a large set of rules; however, you have to add the most precise constraints and order them properly so that no ambiguity may arise.

**Hint**: the YAML syntax allows you to write more legible configuration files if you write associative arrays line by line. For instance, the last rule can also be written: article_by_title_with_date: url: /:year/:month/:day/:title param: module: article action: permalink requirements: year: \d{4}$ month: \d\d$ day: \d\d$

### Default values

Here is a new example:

article_by_id:

url: /article/:id param: { module: article, action: read, id: 1 }

This rule defines the default value for the id argument. This means that a /article/100 URL will behave as previously, but in addition, the URL /article/ will be equivalent to /article/1. The default parameters don't need to be variables found in the pattern. Consider the following example:

article_by_id:

url: /article/:id param: { module: article, action: read, id: 1, display: true }

The display argument will be passed with the value true, whatever the pattern. And, if you look carefully, you will see that article and read are also default values for variables not found in the pattern.

### Default rules

The default routing.yml has a few default rules. To allow the old style 'module/action' URLs to work:

default:

url: /:module/:action/*

As mentioned above, you may need to change this rule if some of your modules have names that can match other patterns.

The other default rules are used to set the root URL to point the default module and action:

homepage:

url: / param: { module: #SF_DEFAULT_MODULE#, action: #SF_DEFAULT_ACTION# }

default_index:

url: /:module param: { action: #SF_DEFAULT_ACTION# }

The default module and action themselves are configured in the settings.yml file.

### How to avoid mentioning the front controller ?

In all previous examples, the URLs still have contain the index.php header to be processed. This is because the front controller has to be called first so that the routing feature can work.

If you have the mod_rewrite module activated, use the following configuration (which is the default configuration bundled with symfony in the myproject/web/.htaccess file) to tell apache to call the index.php file by default:

Options +FollowSymLinks? +ExecCGI

RewriteEngine? On

# we skip all files with .something RewriteCond? %{REQUEST_URI} \..+$ RewriteRule? .* - [L]

# we check if the .html version is here (caching) RewriteRule? $ index.html [QSA] RewriteRule? ([.]+)$ $1.html [QSA] RewriteCond? %{REQUEST_FILENAME} !-f

# if no rule matched the url, we redirect to our front web controller RewriteRule? (.*)$ index.php [QSA,L]

# big crash from our front web controller ErrorDocument? 500 "<h2>Application error</h2>Symfony application failed to start properly"

Now a call to

http://myapp.example.com/article/read/id/100

Will be properly understood as

http://myapp.example.com/index.php/article/read/id/100

Outputting smart URLs


### Matching patterns

Until now, the routing.yml file only helped to reproduce the mod_rewrite behaviour, i.e. understanding properly formatted URLs. The good news is, now that you defined routing rules, they will be automatically used to transform URLs *from* your application.

In symfony, when you write a link in a template, you use the link_to() helper:

[php] <?php echo link_to($article->getTitle(), '/article/read?id='.$article->getId()) ?>

To read more about this helper, check the chapter about [link helpers](templating_link_helpers.txt). With the default routing configuration, this outputs the following HTML code:

[php] <a href="/index.php/article/read/id/100">my_article_title</a>

But since you wrote routing rules, symfony will automatically interpret them in the other way and generate:

[php] <a href="/index.php/article/100">my_article_title</a>

The rules will be parsed with the same order as for the interpretation of an input request, and the first rule matching the arguments of the link_to() second argument will determine the pattern to be used to create the output URL.

### Getting rid of index.php

As for now, the link helpers still output the name of the front controller. If the web server is configured to handle calls without mention of the front controller, as described above, the routing system can be told not to include it.

This is done in the application settings.yml configuration file. To turn off the display of the front controller in the production environment, write:

prod:

.settings

no_script_name: on

### Adding a .html

Having an output URL like:

http://myapp.example.com/2005/06/25/my_article_title

is not bad, but

http://myapp.example.com/2005/06/25/my_article_title.html

is much better. It changes the way your application is perceived by the user, from "a dynamic thing with cryptic calls" to "a deep and well organized web directory". All that with a simple suffix. In addition, the search engines will grant more stability to a page named like that.

As before, this is simply done in the settings.yml configuration file of the application:

prod:

.settings

suffix: .html

The default suffix is set to ., which means that nothing is appended to the end of the routed url. You can specify any type of suffix, including / to have an URL looking like:

http://myapp.example.com/2005/06/25/my_article_title/

It is sometimes necessary to specify a suffix for a unique routing rule. In that case, directly write the suffix in the related url: line of the routing.yml file; the global suffix will be ignored.

article_list_feed:

url: /latest_articles.rss param: { module: article, action: list, type: rss }

update_directory:

url: /updates/ param: { module: update, action: list }

Retrieve information about the current route


If you need to retrieve information about the current route, for instance to prepare a future 'back to page xxx' link, you should use the methods of the sfRouting object. For instance, if your routing.yml defines:

my_rule:

url: /call_my_rule param: { module: mymodule, action: myaction }

Use the following calls in the action:

[php] // if you require an URL like http://myapp.example.com/call_my_rule/param1/xxx/param2/yyy

$uri = sfRouting::getInstance()->getCurrentInternalUri(); // will return 'mymodule/myaction?param1=xxx&param2=yyy'

$uri = sfRouting::getInstance()->getCurrentInternalUri(true); // will return '@myrule?param1=xxx&param2=yyy'

$route = sfRouting::getInstance()->getCurrentRouteName(); // will return 'myrule'

The URIs returned by the ->getCurrentInternalUri() method can be used in a call to a link_to() helper.

In addition, you might want to get the first or the last action called in a template. The following variables are automatically updated at each request and are available to templates:

$sf_first_action $sf_first_module $sf_last_action $sf_last_module

You might ask: Why can't I simply retrieve the current module/action ? Because the calls to actions is a stack, and several calls can be made for a unique request. For instance, if the end of an action contents a forward statement, several actions will be called.