Ha okay, will try to post everything relevant without putting too much in that you don't need (I'm a little worried that you'll come back and say my code is totally awful!) Bear in mind I've cut out a lot of the error checking that's irrelevant to this.
So I have my controllers set up something like this:
=head2 base
Chain base for getting the club ID and checking it. Matches /clubs/*
=cut
sub base :Chained("/") :PathPart("clubs") :CaptureArgs(1) {
my ( $self, $c, $id_or_key ) = @_;
# Load the messages
$c->load_status_msgs;
my $club = $c->model("DB::Club")->find_id_or_url_key( $id_or_key );
if ( defined($club) ) {
# Club found, stash it, then stash the name / view URL in the brea
+dcrumbs section of our stash
$c->stash({
club => $club,
breadcrumbs => {
object => {
label => $club->full_name,
link => $c->uri_for_action("/clubs/view", [$club->url_key])
+,
},
},
});
} else {
# 404
$c->detach( "TopTable::Controller::Root", "default" );
return;
}
}
=head2 view
Chained to the base class; all this does is check we're authorised to
+view clubs, then the more relevant funcionality is in methods chained
+ from this.
=cut
sub view :Chained("base") :PathPart("") :CaptureArgs(0) {
my ( $self, $c ) = @_;
# Check that we are authorised to view clubs
$c->forward( "TopTable::Controller::Users", "check_authorisation", [
+"club_view", "view clubs", 1] );
$c->forward( "TopTable::Controller::Users", "check_authorisation", [
+ [ qw( club_edit club_delete team_view team_create ) ], "edit clubs",
+ 0] );
}
=head2
Get and stash the current season (or last complete one if it doesn't e
+xist) for the team view page. End of chain for /clubs/*
=cut
=head2
Get and stash the current season (or last complete one if it doesn't e
+xist) for the team view page. End of chain for /clubs/*
=cut
sub view_current_season :Chained("view") :PathPart("") :Args(0) {
my ( $self, $c ) = @_;
my $club = $c->stash->{club};
# Try to find the current season (or the last completed season if th
+ere is no current season)
my $season = $c->model("DB::Season")->get_current;
# More season stuff
# Finalise the view routine
$c->detach("view_finalise");
}
=head2 view_specific_season
View a club only with teams for a specific season. Matches /clubs/*/s
+easons/* (End of chain)
=cut
sub view_specific_season :Chained("view") :PathPart("seasons") :Args(1
+) {
my ( $self, $c, $season_id_or_url_key ) = @_;
my $club = $c->stash->{club};
# Validate the passed season ID
my $season = $c->model("DB::Season")->find_id_or_url_key( $season_id
+_or_url_key );
if ( defined($season) ) {
# More season stuff
# Append the season on to the object for breadcrumbs - this is for
+ using in sprintf for our text labels
$c->stash->{breadcrumbs}{replace_text} = $season->name;
}
# Finalise the view routine
$c->detach("view_finalise");
}
sub view_finalise :Private {
# Irrelevant to this
}
My breadcrumbs code is:
# Get the action for the current request and join it on to the names
+pace (unless they match, as that means we'll get multiples where we d
+on't want it; if the action has a slash in it, we won't join either)
my $action = $c->request->action;
$action = join("/", $c->namespace, $action) unless $action eq $c->
+namespace or $action =~ m#/#;
my @paths = split("/", $action);
# This array will hold the breadcrumbs bits in reverse order (we w
+ill reverse it when we stash it)
my @breadcrumbs = ();
my $i = 0; # Counter to tell if we're on the first bit - if we are
+, don't link it, as that's the current page
while( my $label = pop(@paths) ) {
# Skip if we're hiding indices and this is an index
next if $label eq "index" and $c->config->{breadcrumbs}{hide_ind
+ex};
# The path is all of the @path elements we haven't popped off so
+ far, plus the current path elemtn (currently in $label)
# joined with slashes.
my $path = join( "/", @paths, $label );
# Build the label
$label = $c->forward( "breadcrumbs_label_for", [$path, $label] )
+;
# Now that we have the label for this path, we can get rid of th
+e path if this is the first iteration, so we don't link to it
$path = "" unless $i;
# Push the path and label on to our breadcrumbs array if we have
+ a label (if it's blank, assume we don't want to display it)
push(@breadcrumbs, {
path => $path,
label => $label,
}) if $label;
unless ( $i ) {
# We need to set up the object after the first iteration so th
+at we can have something like 'path > object > action (view, edit, et
+c)'
if ( exists( $c->stash->{breadcrumbs}{object} ) ) {
my $object = $c->stash->{breadcrumbs}{object};
# Check if we have a link and we're not currrently viewing t
+he object (no point linking the object if we're already viewing it)
if ( exists( $object->{link} ) and $c->request->action !~ /(
+view|view_current_season|view_specific_season)$/ ) {
# Push the path and label on to our breadcrumbs array
push(@breadcrumbs, {
path => $object->{link},
label => $object->{label},
});
} else {
# Push label only on to our breadcrumbs array
push(@breadcrumbs, {
label => $object->{label},
});
}
}
}
$i++; # Increment the counter
}
# Add the home element if we need
unless ( $c->config->{breadcrumbs}{hide_home} ) {
my $label = $c->forward( "breadcrumbs_label_for", [qw( / home )
+]);
push(@breadcrumbs, {
path => "/",
label => $label
});
}
=head2 breadcrumbs_label_for
Generate labels for specific paths in the breadcrumbs.
=cut breadcrumbs_label_for
sub breadcrumbs_label_for :Private {
my ( $self, $c, $path, $label ) = @_;
# If we have a customised label for this path, return it
if ( exists( $c->config->{breadcrumbs}{labels}{$path} ) ) {
if ( exists( $c->stash->{breadcrumbs}{replace_text} ) ) {
# Replacement text, sprintf it
# First check if the replacement text is an array, if not, make
+it one - this makes it easier to pass in both and not care which is w
+hich from now on
$c->stash->{breadcrumbs}{replace_text} = [ $c->stash->{breadcrum
+bs}{replace_text} ] if ref( $c->stash->{breadcrumbs}{replace_text} )
+ne "ARRAY";
# Now return the sprintf'd version of the label
return sprintf( $c->config->{breadcrumbs}{labels}{$path}, @{ $c-
+>stash->{breadcrumbs}{replace_text} } );
} else {
# No replacement text, just return it
return $c->config->{breadcrumbs}{labels}{$path};
}
}
# If there's no label specified, we use the path. Replace underscor
+es with spaces and convert the first following character to uppercase
$label =~ s/_(.)/" " . uc($1)/eg;
# Return the value with the first character converted to upper-case
+(unless we've specified all lower-case)
return !$c->config->{breadcrumbs}{lowercase} ? ucfirst($label) : lc(
+$label);
}
Hope all this makes sense - genuinely a little worried about showing my code to genuine Perl gurus...! Eventually I think I want to get to something like this:
controller > object (looked up from base method) > action > 'Seasons' > season
controller > object > action
etc.
My plan was to split the action up into its individual parts and hopefully do it that way, but I'm getting the previously stated error when I try and do this. |