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 {