Yesterday, I have committed the first slew of changes to the routing framework. Thanks to this refactoring, developers have new opportunities to customize the routing and this will allow very cool features in the very near future.
But today, let's dive into the goodness of the symfony 1.2 routing framework.
Routes as first-class objects
Before symfony 1.2, the routing system (via the sfPatternRouting
class) stored its routes as an associative array. It works quite well but does not allow for easy customization. To give developers the freedom to easily enhance the routing process, the routes are now stored as an array of sfRoute
objects.
This refactoring is totally backward compatible and nothing need to be changed in your routing.yml
configuration file.
If you connect your routes with PHP code, you must now pass a
sfRoute
instance as the second argument to theconnect()
,preprendRoute()
,appendRoute()
, andinsertRouteBefore()
methods:$routing->connect('foo_bar', new sfRoute('/foo/:bar', array('module' => 'foo', 'action' => 'bar')));
Route customization
The sfRoute
constructor takes an array of options as its last argument to allow the customization of each route.
In the routing.yml
configuration file, use to options
key to override defaults:
article: url: /article/:id-:slug options: { segment_separators: [/, ., -] }
The segment_separators
option sets the characters that can separate each segment of the pattern. In the previous example,
we explicitly add the -
(dash) character as a valid separator to the two default built-in separators (/
and .
).
This allows a URL like /article/1-my_article_title
to match the route with an id
variable of 1
and a slug
variable of
my_article_title
.
This option was already available in symfony 1.1, but you were only allowed to change it globally. By adding new separators globally, you could have potentially broken third-party routes (like the ones defined in plugins).
Beside the options already available, there are two new options:
generate_shortest_url
: Whether to generate the shortest URL possibleextra_parameters_as_query_string
: Whether to generate extra parameters as a query string
These options can be configured globally for the routing or locally for each route.
By default, they are set to false
in factories.yml
to keep backward compatibility with previous symfony versions.
Let's see how to use these options to customize a route:
articles: url: /articles/:page param: { module: article, action: list, page: 1 } options: { generate_shortest_url: true }
This route will generate the shortest URL possible. So, if you pass a page
of 1
, which is the default value for the page
variable, the generated URL will be /articles
:
echo url_for('@articles?page=1'); // generates /articles // would have been /articles/1 in symfony 1.1 echo url_for('@articles?page=2'); // generates /articles/2
Let's take another example to illustrate extra_parameters_as_query_string
usage:
articles: url: /articles options: { extra_parameters_as_query_string: true }
This route will accept extra parameters that are not valid variables for the route and add them as a query string:
echo url_for('@articles?page=1'); // generates /articles?page=1 // would not have matched the route in symfony 1.1 echo url_for('@articles?page=2'); // generates /articles?page=2
As this option can change the matching route depending on your configuration, enable it with care if you are upgrading an existing project. But as the test browser uses the routing process, you will just have to launch your functional test suite to check if it breaks something.
Routing customization
All the logic from the sfPatternRouting
routing class has been moved to the sfRoute
object:
- When an HTTP request comes in, the routing object asks each route in turn if it matches the URL.
- And when you want to generate a URL, the routing object asks each route in turn if it is able to generate a URL for the given parameters.
By embarking all the logic in the route class, it is just a matter of creating a new route class to change the behavior of the parsing or generation process.
If you want to change the default route class used by a route, add a class
key to your route configuration like this:
article: url: /article/:id param: { module: article, action: index } class: myRoute
With this routing configuration, symfony will use the myRoute
class for the article
route, instead of the built-in sfRoute
class. It is now up to you to override the default behaviors.
The
sfRoute
class is much more modular than the oldsfPatternRouting
class to allow easier customization of the default behavior. The "compilation" phase has been refactored into smaller methods, the code has been simplified, and it is now based on a "real" tokenizer.
The built-in sfRequestRoute
Symfony has another built-in route, sfRequestRoute
, which can enforce the HTTP method during the matching process:
article: url: /article/:id requirements: { sf_method: get } class: sfRequestRoute
With the previous routing configuration, the article
route will only match
requests with a GET
HTTP method.
If you define several routes with the same url
but different method requirements, you can pass sf_method
as a parameter
when you generate a route:
<?php echo link_to('Great article', '@article?id=1&sf_method=get')) ?>
This is made possible because the routing is now aware of the request context. When the request calls the routing, it passes the following context:
method
: The HTTP methodformat
: The request formathost
: The hostnameis_secure
: Whether the request was called with HTTPS or notrequest_uri
: The full request URIprefix
: The prefix to add to each generated route
The sfRequestRoute
is the first step towards a RESTful architecture.
What's next?
In the next part, we will see how symfony manages
resources by automatically generating RESTful routes based on a simple configuration of routing.yml
.
We will also learn how to cut the code you have to write in your actions, thanks to the built-in integration of Propel and Doctrine.
Last but not least, I will talk about the new routing framework at symfonyCamp with even more examples and a live demo. So, if you want to learn more about symfony 1.2 and if you are not already registered, you can still join the 60 people who will be there to share their symfony experience.
If you want to follow the progress of symfony 1.2 or if you want to know all the changes we have made for symfony 1.2, you can periodically check the upgrade to symfony 1.2 page where all changes and news features are documented in real-time.
great addition!
Love the way to put GET parms in the URLs, the new routing changes sound great.
Well done :)
Wahou that's awesome :)
Fabien, do you speak about Symfony 1.2 at Forum PHP Paris in december ?
Sounds great, i like the ability to choose to have get parameters or "routing parameters"
Is that to say, in Symfony 1.2, it is easy to make our program into REST architecture by the new routing systems?
extra_parameters_as_query_string is what I was waiting for a long time and was making me add extra routes for the fact we could not add extra parameters for routes. Nice one.
How these changes will impact the performance?
Malas: your developping performances will be greatly improved.
@Malas: The routes objects are cached. And in the near future, we will only unserialize the route objects we need. So, it will perform very well.
I am eagerly awaiting part 2. Any word on when that'll be?