Hi Sam, at all, I have developed a patch to enable sorting within loops specifying the sort criteria within the template - don't know how useful others will find it, but i can think of it being useful to the designers around here anyway (and to me displaying the data sorted in different ways is a design issue for the most part, hence the development of the patch).
>From the Documentation - Part of the patch: ========================================================================= You can also sort the data within loops based on certian criteria. Aka, you can do the following: <TMPL_LOOP NAME=PRODUCTS SORTBY="name;order=desc;type=alpha"> Name : <TMPL_VAR NAME=NAME> Description: <TMPL_VAR NAME=DESCRIPTION> ID : <TMPL_VAR NAME=ID> </TMPL_LOOP> The above will sort the loop on the "name" field, in ascending order based on alphanumeric sort. Please see the documentation for the sort_data() subroutine to see what is available as arguments to "order" and "type". Note: You can leave the order and type options out and detauls of "desc" and "alpha" will be used for them. Bugs: If you use the same loop multiple times in your template, the sort criteria for the very first loop will apply. (aka, any sort criteria given with the second/subsequent usage of the loop in the same template will be ignored). ========================================================================= Do others think its a useful feature/patch? If so, Sam, can i please request for it to be patched into the core. thanks, simran.
*** Template.pm.orig Thu Feb 6 13:44:20 2003 --- Template.pm Thu Feb 6 16:10:42 2003 *************** *** 1,5 **** --- 1,7 ---- package HTML::Template; + use Data::Dumper; + $HTML::Template::VERSION = '2.6'; =head1 NAME *************** *** 248,253 **** --- 250,277 ---- call. If you want your variables to be global you can use 'global_vars' option to new() described below. + You can also sort the data within loops based on certian criteria. + Aka, you can do the following: + + <TMPL_LOOP NAME=PRODUCTS SORTBY="name;order=desc;type=alpha"> + Name : <TMPL_VAR NAME=NAME> + Description: <TMPL_VAR NAME=DESCRIPTION> + ID : <TMPL_VAR NAME=ID> + </TMPL_LOOP> + + The above will sort the loop on the "name" field, in ascending order + based on alphanumeric sort. Please see the documentation for + the sort_data() subroutine to see what is available as arguments + to "order" and "type". + + Note: You can leave the order and type options out and detauls + of "desc" and "alpha" will be used for them. + + Bugs: If you use the same loop multiple times in your template, + the sort criteria for the very first loop will apply. + (aka, any sort criteria given with the second/subsequent + usage of the loop in the same template will be ignored). + =head2 TMPL_INCLUDE <TMPL_INCLUDE NAME="filename.tmpl"> *************** *** 1079,1084 **** --- 1103,1109 ---- # to find, by the way. delete $self->{cache} if $options->{shared_cache}; + return $self; } *************** *** 1903,1910 **** \s* (?:--)?> ! (.*) # $19 => $post - text that comes after the tag $/sx) { $which = uc($1); # which tag is it --- 1928,1950 ---- \s* + # SORTBY attribute + (?: + [Ss][Oo][Rr][Tt][Bb][Yy] + \s*=\s* + (?: + "([^">]*)" # $19 => double-quoted DEFAULT value " + | + '([^'>]*)' # $20 => single-quoted DEFAULT value + | + ([^\s=>]*) # $21 => unquoted DEFAULT value + ) + )? + + \s* + (?:--)?> ! (.*) # $22 => $post - text that comes after the tag $/sx) { $which = uc($1); # which tag is it *************** *** 1922,1928 **** defined $16 ? $16 : defined $17 ? $17 : defined $18 ? $18 : undef; ! my $post = $19; # what comes after on the line # allow mixed case in filenames, otherwise flatten $name = lc($name) unless (not defined $name or $which eq 'TMPL_INCLUDE' or $options->{case_sensitive}); --- 1962,1970 ---- defined $16 ? $16 : defined $17 ? $17 : defined $18 ? $18 : undef; ! my $sortby = defined $19 ? $19 : defined $20 ? $20 : defined $21 ? $21 : undef; ! ! my $post = $22; # what comes after on the line # allow mixed case in filenames, otherwise flatten $name = lc($name) unless (not defined $name or $which eq 'TMPL_INCLUDE' or $options->{case_sensitive}); *************** *** 1986,1994 **** die "HTML::Template->new() : Already used param name $name as a TMPL_VAR, TMPL_IF or TMPL_UNLESS, found in a TMP_LOOP at $fname : line $fcounter!"; } else { # store the results in a LOOP object - actually just a # thin wrapper around another HTML::Template object. - $loop = HTML::Template::LOOP->new(); $pmap{$name} = $loop; } --- 2028,2059 ---- die "HTML::Template->new() : Already used param name $name as a TMPL_VAR, TMPL_IF or TMPL_UNLESS, found in a TMP_LOOP at $fname : line $fcounter!"; } else { + # + # If we are sorting the data ... then save the sort parameters with the loop information... + # + if ($sortby) { + my ($sortfield, $options) = split(';', $sortby, 2); + my $sortorder = "desc"; + my $sorttype = "alpha"; + + foreach (split(';', $options)) { + my ($option, $value) = split('=', $_, 2); + if ($option =~ /^\s*order\s*$/i) { ($sortorder = $value) =~ s/(^\s*|\s*$)//g; } + elsif ($option =~ /^\s*type\s*$/i) { ($sorttype = $value) =~ s/(^\s*|\s*$)//g; } + } + + die "HTML::Template - invalid loop sortby parameters at $fname : line $fcounter!" + if ($sortorder !~ /^(desc|asc)$/i || $sorttype !~ /^(numeric|alpha)$/i); + + $loop = HTML::Template::LOOP->new({sortfield => $sortfield, sorttype => $sorttype, sortorder => $sortorder}); + } + else { + $loop = HTML::Template::LOOP->new({}); + } + + # store the results in a LOOP object - actually just a # thin wrapper around another HTML::Template object. $pmap{$name} = $loop; } *************** *** 2464,2469 **** --- 2529,2547 ---- if (defined($value_type) and length($value_type) and ($value_type eq 'ARRAY' or ((ref($value) !~ /^(CODE)|(HASH)|(SCALAR)$/) and $value->isa('ARRAY')))) { (ref($param_map->{$param}) eq 'HTML::Template::LOOP') or croak("HTML::Template::param() : attempt to set parameter '$param' with an array ref - parameter is not a TMPL_LOOP!"); + + # + # Sort the fields (if a sortby was specified...) + # + { + my $sortfield = $param_map->{$param}->[0]->{'sortfield'}; + my $sorttype = $param_map->{$param}->[0]->{'sorttype'}; + my $sortorder = $param_map->{$param}->[0]->{'sortorder'}; + if ($sortfield) { + $value = $self->sort_data(data => $value, sortfield => $sortfield, sorttype => $sorttype, sortorder => $sortorder); + } + } + $param_map->{$param}[HTML::Template::LOOP::PARAM_SET] = [@{$value}]; } else { (ref($param_map->{$param}) eq 'HTML::Template::VAR') or *************** *** 2475,2480 **** --- 2553,2626 ---- =pod + =head2 sort_data() + + sort_data() sorts given data (data structure as is expected by the <TMPL_LOOP> parameter) based on the specified + criteria. + + Input: Hash of key/value pairs as described below. + data [mandetory] => The data you want to sort + sortfield [mandetory] => The field/column you want to sort by + sorttype [optional: default - alpha] => 'numeric' or 'alpha' depending on if you want numeric or character sort + sortorder [optional: default - desc] => 'asc' or 'desc' depending on if you want ascending or descending results + + Return: ArrayREF + $data => The data in the same structure as the data you gave as input, but sorted based on the critirea supplied. + + =cut + + sub sort_data { + my $self = shift; + my %options = @_; + + # + # + # + my $sortfield = $options{'sortfield'} || die "Must specify sort field/column"; + my $sorttype = $options{'sorttype'} || 'alpha'; + my $sortorder = $options{'sortorder'} || 'desc'; + my $data = $options{'data'} || die "Must provide data"; + + # + # + # + die "Data must be ARRAYREF" if (ref($data) ne 'ARRAY'); + + # + # Do the actual sorting... + # + if ($sorttype eq 'alpha') { + if ($sortorder eq 'asc') { + # or with zero to avoid compiler warnings on comparing undefined values + $data = [ sort{ ( $a->{$sortfield} or 0) cmp ( $b->{$sortfield} or 0) } @$data ]; + } + elsif ($sortorder eq 'desc') { + $data = [ sort{ ( $b->{$sortfield} or 0) cmp ( $a->{$sortfield} or 0) } @$data ]; + } + else { + die "Invalid sort order $sortorder"; + } + } + elsif ($sorttype eq 'numeric') { + if ($sortorder eq 'asc') { + $data = [ sort{ ( $a->{$sortfield} or 0 ) <=> ( $b->{$sortfield} or 0 ) } @$data ]; + } + elsif ($sortorder eq 'desc') { + $data = [ sort{ ( $b->{$sortfield} or 0 ) <=> ( $a->{$sortfield} or 0 ) } @$data ]; + } + else { + die "Invalid sort order $sortorder"; + } + } + else { + die "Invalid sort type $sorttype"; + } + + return $data; + } + + =pod + =head2 clear_params() Sets all the parameters to undef. Useful internally, if nowhere else! *************** *** 2876,2882 **** package HTML::Template::LOOP; sub new { ! return bless([], $_[0]); } sub output { --- 3022,3029 ---- package HTML::Template::LOOP; sub new { ! my $hashref = $_[1]; ! return bless([$hashref], $_[0]); } sub output {