Doing rails-like RESTful resources is not natively possible with symfony, but with a bit of tweaking you can get closer to it.
The problem
Thanks to routing requirements, you can restrict the cases where a route matches a URL.
show_article:
url: /article/:id
params: { module: article, action: show }
requirements: { id: \d+ }
What I sometimes miss, is the ability to add a HTTP request method requirement, i.e. making a route match only if the request is GET, or POST, or DELETE. This is really useful when designing RESTful resources, where a single external URL can match more than one resource, depending on the request method:
show_article:
url: /article/:id
params: { module: article, action: show }
requirements: { id: \d+, sf_method: get }
update_article:
url: /article/:id
params: { module: article, action: update }
requirements: { id: \d+, sf_method: post }
delete_article:
url: /article/:id
params: { module: article, action: delete }
requirements: { id: \d+, sf_method: delete }
The solution
In order to allow symfony to do that, you need to modify two symfony classes: sfWebRequest and sfPatternRouting. As both of these classes are controlled by a factory in symfony 1.1, I recommend creating two custom classes, one for the request and one for the routing. Download these two classes here, and place them under the application lib/ folder. Now all you need is to edit your factories.yml to use these classes instead of the default ones:
all:
request:
class: myWebRequest
routing:
class: myPatternRouting
Clear the cache, and now you can add method requirements to your routing rules, as explained above.
In order to create a link to a route with a requirement other than sf_method: get, you must pass the sf_method parameter to url_for() explicitly (don't worry, it will not end up in the external URL):
echo url_for
('article/show?id=1') => /article/
1
echo url_for
('article/update?id=1') => fails
echo url_for
('article/update?id=1&sf_method=post') => /article/
1
echo url_for
('article/delete?id=1') => fails
echo url_for
('article/delete?id=1&sf_method=post') => /article/
1
What's next
Note that this is just a proof-of-concept, and to be perfectly RESTful, link_to() should generate a form doing a POST request when sf_method=post is passed. Also, the routing configuration handler could be modified to transform a single RESTful rule:
article:
restful:
base_url: /articles
module: article
identifier: id
Into a list of normal rules:
# display a list of articles
list_article:
url: /articles
params: { module: article, action: index }
requirements: { sf_method: get }
# display a single article
show_article:
url: /articles/:id
params: { module: article, action: show }
requirements: { id: \d+, sf_method: get }
# display an empty form for a new article
new_article:
url: /articles/new
params: { module: article, action: new }
requirements: { sf_method: get }
# handle the submission of a new article form
create_article:
url: /articles/new
params: { module: article, action: create }
requirements: { sf_method: post }
# display a form to edit an existing article
edit_article:
url: /articles/:id/edit
params: { module: article, action: edit }
requirements: { id: \d+, sf_method: get }
update_article:
url: /articles/:id/edit
params: { module: article, action: update }
requirements: { id: \d+, sf_method: post }
delete_article:
url: /articles/:id
params: { module: article, action: delete }
requirements: { id: \d+, sf_method: delete }
I leave that to your sagacity.