Day 13 - $c->uri_for fun and profit
Today we will take a trip into the wonderful and mystical world of uri_for. The basic premise behind uri_for is to make generating URIs for actions with parameters easy and fun (well as fun as URIs get).
Basic uri_for
$c->uri_for will return the URL for the current actions namespace, so, if you are in Controller MyApp::Children it will return:
$c->uri_for() http://localhost:3000/children
for any action you are running in that controller. If you wanted to get to a specific action you can do
$c->uri_for('good') http://localhost:3000/child/good
What if I wanted to get outside of the 'child' namespace? well, use a / to root it.
$c->uri_for('/elves') http://localhost:3000/elves
For most cases this simple construct is more than adequate but a more canonical way is via the action_for construct for instance, If in my controller i have:
sub deliverto :Path('good') { ... }
I could refer to this action in the current namespace as follows:
$c->uri_for($c->action_for('deliverto')) http://localhost:3000/children/good
Or in another namespace
$c->uri_for($c->controller('elves')->action_for('sweatshop')) http://localhost:3000/elves/jobs
If, in the future i decided to change my URI structure all the references to this controller would update accordingly.
Adding parameters
You can easily add parameters to the uri_for object like this:
$c->uri_for('house',{fire => 0, mincepie => 1}); http://localhost:3000/house?fire=0&mincepie=1
or even in a TT template:
[% c.uri_for('house',{fire => 0, mincepie => 1}) %] http://localhost:3000/house?fire=0&mincepie=1
or you could use the params that were passed into the page to re-populate it
$c->uri_for('house',$c->req->params) %] http://localhost:3000/house/?a=b&c=d (assuming parameters passed in to page were a=b c=d)
Complex paths with captures
uri_for makes it incredibly easy to create and adjust paths for chained actions, for instance from
http://localhost:3000/house/4/address $c->uri_for($c->action,$c->req->captures,'print');
would fill out all the captures for the previous request and add print to it:
http://localhost:3000/house/4/address/print
But what if you wanted to change some of the captures... say for linking from a list - well it's just an array so thats easy:
$c->uri_for($c->action,[2],'new'); http://localhost:3000/house/2/address/print
uri_for internals
uri_for returns a normalised URI object so you can do all the usual tricks, for instance, say you were wanting to go to a secure section of your catalyst site you could do the following :
my $uri = $c->uri_for('secure'); $uri->scheme('https'); https://localhost:3000/letters/list
Useful code snippets
You could add these in your main application class (e.g. MyApp.pm) to make them available for all your actions.
sub action_uri { my ($c, $controller, $action, @params) = @_; return $c->uri_for($c->controller($controller)->action_for($action), @params); } (thanks zamolxes!) sub redirect_to_action { my ($c, $controller, $action, @params) =@_; $c->response->redirect($c->uri_for($c->controller($controller)->action_for($action), @params)); $c->detach; } (thanks zamolxes!) sub chained_uri_for { my $c = shift; return $c->uri_for($c->action,$c->req->captures,@_); } <a href="[% c.action_uri('Foo::Bar','baz',5) %]">BAZ!</a> $c->redirect_to_action('User','login');
AUTHOR
Simon Elliott (purge, cpan@browsing.co.uk)