I have this piece of code as part of a foreach loop in my controller:
my $gr = My::Model::Group->new(id => $gra->gr_id);
$gra = My::Model::Group::Admin->new(id => $gra->id);
push(@$groups, {$gr => $gra});
In @$groups array I want to store anonymous hashes where the key element is the group object and the value element is the admin of that group object. Then in the template I want to show the list of different groups that the admin can log in, for that I have this code:
[%- FOREACH gr IN groups -%]
<li><input type="radio" name="group" value="[% gr.id %]">[% gr.name %]</input></li>
[%- END -%]
I know that the p IN partners is not right but is to show you what I want to achieve. Any suggestions on the template code?
You will need to rework your code significantly to make this possible.
Keys in Perl hashes are strings , not scalars. Using anything that isn't a string as a key in a hash (eg, $gr
in the expression { $gr => $gra }
will cause it to be stringified, just as if you had interpolated it into a string or printed it. Unless you have explicitly overloaded the ""
operator on the My::Model::Group
object, the key will end up being stored as a literal string along the lines of:
"My::Model::Group=HASH(0x1234567890)"
This string cannot be converted back to the original object -- in fact, the original object was probably garbage-collected as soon as it went out of scope, so it no longer exists at all.
Consider storing the pair as an array reference instead, eg
push @$groups, [$gr, $gra];
duskwuff already explains in their answer that you can't use objects as hash keys as they get serialized and you'll lose the object-ness. My answer builds on that.
Let's say you have an array of arrays instead, where each inner array holds a pair of objects. I've created Moo classes to illustrate.
package My::Model::Group;
use Moo;
has [qw/id name/] => ( is => 'ro' );
package My::Model::Group::Admin;
use Moo;
has [qw/id name/] => ( is => 'ro' );
package main;
my $groups = [
[
My::Model::Group->new( id => 1, name => 'group1' ) =>
My::Model::Group::Admin->new( id => 1, name => 'foo' )
],
[
My::Model::Group->new( id => 2, name => 'group2' ) =>
My::Model::Group::Admin->new( id => 1, name => 'foo' )
],
[
My::Model::Group->new( id => 3, name => 'group3' ) =>
My::Model::Group::Admin->new( id => 1, name => 'bar' )
],
[
My::Model::Group->new( id => 4, name => 'group4' ) =>
My::Model::Group::Admin->new( id => 1, name => 'foo' )
],
];
There are four pairs. Two admins, four groups. Three of the groups belong to the foo admin, and one to bar . Now let's look at the template.
use Template;
my $tt = Template->new();
$tt->process( \*DATA, { groups => $groups }, \my $output )
or die $tt->error;
print $output;
__DATA__
[%- FOREACH item IN groups -%]
[%- DEFAULT by_admin.${item.1.name} = [] -%]
[%- by_admin.${item.1.name}.push(item.0) -%]
[%- END -%]
[%- FOREACH admin IN by_admin.keys.sort -%]
[%- FOREACH group IN by_admin.$admin -%]
[%- admin %] -> [% group.id %]
[%- END -%]
[%- END -%]
The relevant part obviously is the DATA
section. We need to reorganize the array data structure into a hash that has the admins, and then each group sorted into one of the admin slots.
We don't need to create the by_admin
variable. It will be created globally implicitly. But we do need to set a default value for $by_admin->{$item[0]->name}
(I'm using Perl syntax now, to make it easier to understand). It seems like Template Toolkit does not know autovivification , and the DEFAULT
keyword is similar to the //=
assignment operator in Perl .
We can then push
the first element of item
into the array ref we just created (if it didn't exist yet) inside the hash ref element with the key item.1.name
inside by_name
.
Once we're done preparing, the rest is just a simple loop. We iterate the sort
ed keys
of by_admin
, and then iterate the array ref that's behind that key.
Here's the output:
bar -> 3
foo -> 1
foo -> 2
foo -> 4
It would make sense to do the preprocessing not in a template, but in your controller instead. As normal Perl code it should be easier to read.
my %by_admin;
for my $group (@$groups) {
push @{ $by_admin{ $group->[1]{name} } }, $group->[0];
}
Note that I have omitted use strict
and use warnings
for brevity.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.