Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

[Catalyst] How do you handle "URL Parameters" (aka advanced "route matching")

by three18ti (Scribe)
on Apr 03, 2014 at 12:26 UTC ( #1080942=perlquestion: print w/replies, xml ) Need Help??
three18ti has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks

I'm working on an internal application, I think calling it a blog "engine" is a bit too generous... I'm basically looking to build a very basic "web log" (literally, where we can log our work via web interface)

I'd like to be able to pull logs (they really aren't articles, more just "notes") based on the URL, e.g. http://address/$year/$month/$date/$title so http://address/2014/01/10/foo-bar-baz would pull the log from 10 Jan. 2014 about foo bar baz.

In something like dancer, this is really easy to do with route "placeholders", e.g.:

get '/:year/:month/:day' => sub { my $year = params->{year}; my $month = params->{month}; ... }


get '/*/*/*' => sub { my ($year, $month, $day) = splat; ... }

is there any way to do something similar in Catalyst? From my googling, it seems like "chained actions" are the way to go for this behavior, but they always seem like a black magic and make debugging a terrible pain in the arse... (maybe I'm just doing it wrong...?)

(I'm certainly not set on Catalyst, I've not done anything really advanced with either Dancer or Catalyst, for instance:, but I can't for the life of me get Dancer to play nice with Moose (I'd prefer to deal with objects for everything since I'm using HTML::FormHandler and a couple other OO modules), and I like Catalyst better because I can break out my logic into multiple files instead of having everything in one gigantic file like Dancer requires (or well, seems to require, if I can break out my logic into multiple "controllers" like I can with Catalyst, then I'm doing it wrong. None of the dancer tutorials I've seen make use of this feature))


Maybe this is something:

But that seems like it would look for a specific string in that parameter, so I'd need 12 routes... e.g.,

sub jan : Regex('^01$') { my ($self, $context, $bar, $baz) = @_; } sub feb : Regex('^02$') { my ($self, $context, $bar, $baz) = @_; }

Which is not what I'm trying to accomplish. (There has to be a better way)

EDIT 2: maybe I should just use get parameters:

Replies are listed 'Best First'.
Re: [Catalyst] How do you handle "URL Parameters" (aka advanced "route matching")
by Theodore (Monk) on Apr 03, 2014 at 13:11 UTC

    In Catalyst, you can use something like:

    sub entry :Args(4) { my ($self, $c, $year, $month, $date, $title) = @_; [locate and print article based on year etc] }
    for a url like:
    http://address/entry/year/month/date/title http://address/entry/2014/04/03/foo
    See the documentation about :Args. Arguments are also found under $c->request->arguments.

      That's too easy.

      Thanks a million!

Re: [Catalyst] How do you handle "URL Parameters" (aka advanced "route matching")
by Your Mother (Chancellor) on Apr 04, 2014 at 17:36 UTC

    Regular expressions in URLs are frowned up in Cat (and I agree; its harder to get right than other approaches). The package that does it was even split out of the core last year. I have done this kind of date thing before and this is what Id recommend.

    • Explicitly take 3 args for year, month, date (you can pare this out into other controllers too; one for year index, one for month index, and the original for a days view/index).
    • Validate that is it a real date with Date::Calcs check_date. Invalid date yields a 404.
    • Then use the known real date for whatever model lookup (DB/file/etc), then present results, no results yields a 404.
    package MyApp::Controller::WhatEver; use warnings; use strict; use parent "Catalyst::Controller"; use Date::Calc "check_date"; sub by_date : Path("d") Args(3) { my ( $self, $c, $yyyy, $mm, $dd ) = @_; $c->detach( YOUR_404_HANDLER ) unless eval { check_date($yyyy,$mm, +$dd) }; my $some_rs = $c->model("Your::Schema")->search({ ...use dates... +}); $some_rs->count || $c->detach( YOUR_404_HANDLER ); }

    Update (2014-07-26): Sloppy. check_date is fatal with bad input, added eval, See also: Re: Script to validate date fails.

Re: [Catalyst] How do you handle "URL Parameters" (aka advanced "route matching")
by Anonymous Monk on Apr 03, 2014 at 14:18 UTC
    In addition, you should specify the URL using a regex pattern that, to the maximum extent feasible, does verify the format of the various URL parameters, such that it will successfully match only a well-formed URL ... $year is exactly four digits, and so on.

      At Theodore recommendation I'm headed off to check the args docs, but is there an easy way you can recommend to use regex with the Args() parameter?

Re: [Catalyst] How do you handle "URL Parameters" (aka advanced "route matching")
by Anonymous Monk on Apr 03, 2014 at 19:36 UTC
    You do not need 12 different regexes to search for \d{2} or \d\d ...

      Indeed, good point. Thanks! (It was late here when I typed my first response)

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1080942]
Front-paged by Arunbear
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (5)
As of 2017-03-30 23:16 GMT
Find Nodes?
    Voting Booth?
    Should Pluto Get Its Planethood Back?

    Results (364 votes). Check out past polls.