在 Perl 的 Template Toolkit 中访问作为匿名散列的数组项
Access an array item that is an anonymous hash in Template Toolkit in Perl
我将这段代码作为控制器中 foreach 循环的一部分:
my $gr = My::Model::Group->new(id => $gra->gr_id);
$gra = My::Model::Group::Admin->new(id => $gra->id);
push(@$groups, {$gr => $gra});
在@$groups 数组中,我想存储匿名散列,其中键元素是组对象,值元素是该组对象的管理员。然后在模板中我想显示管理员可以登录的不同组的列表,因为我有这个代码:
[%- FOREACH gr IN groups -%]
<li><input type="radio" name="group" value="[% gr.id %]">[% gr.name %]</input></li>
[%- END -%]
我知道p IN小伙伴说的不对但是就是想告诉你我想要达到的目的。对模板代码有什么建议吗?
您需要大量修改代码才能实现这一点。
Perl 散列中的键是 字符串 ,而不是标量。使用任何不是字符串的东西作为散列中的键(例如,表达式 { $gr => $gra }
中的 $gr
将导致它被字符串化,就像您将它插入到字符串中或打印出来一样. 除非您在 My::Model::Group
对象上显式重载了 ""
运算符,否则密钥最终将按照以下行存储为文字字符串:
"My::Model::Group=HASH(0x1234567890)"
此字符串无法转换回原始对象 -- 事实上,原始对象可能在超出范围后立即被垃圾回收,因此它根本不存在。
考虑将该对存储为数组引用,例如
push @$groups, [$gr, $gra];
您不能将对象用作散列键,因为它们已序列化,您将失去对象特性。我的回答以此为基础。
假设您有一个数组数组,其中每个内部数组包含一对对象。我创建了 Moo 类 来说明。
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' )
],
];
有四对。两个管理员,四个组。其中三个组属于 foo 管理员,一个属于 bar。现在让我们看一下模板。
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 -%]
相关部分显然是 DATA
部分。我们需要将数组数据结构重新组织成一个包含管理员的哈希,然后将每个组排序到一个管理员槽中。
我们不需要创建 by_admin
变量。它将在全局隐式创建。但是我们确实需要为 $by_admin->{$item[0]->name}
设置一个默认值(我现在使用 Perl 语法,以便于理解)。似乎 Template Toolkit 不知道 autovivification, and the DEFAULT
keyword is similar to the //=
assignment operator in Perl.
然后我们可以 push
item
的第一个元素到我们刚刚创建的数组 ref 中(如果它还不存在)在哈希 ref 元素中,键为 item.1.name
里面by_name
.
准备工作完成后,剩下的就是一个简单的循环。我们迭代 by_admin
的 sort
ed keys
,然后迭代该键后面的数组 ref。
这是输出:
bar -> 3
foo -> 1
foo -> 2
foo -> 4
不在模板中而是在您的控制器中进行预处理是有意义的。作为普通的 Perl 代码,它应该更容易阅读。
my %by_admin;
for my $group (@$groups) {
push @{ $by_admin{ $group->[1]{name} } }, $group->[0];
}
请注意,为了简洁起见,我省略了 use strict
和 use warnings
。
我将这段代码作为控制器中 foreach 循环的一部分:
my $gr = My::Model::Group->new(id => $gra->gr_id);
$gra = My::Model::Group::Admin->new(id => $gra->id);
push(@$groups, {$gr => $gra});
在@$groups 数组中,我想存储匿名散列,其中键元素是组对象,值元素是该组对象的管理员。然后在模板中我想显示管理员可以登录的不同组的列表,因为我有这个代码:
[%- FOREACH gr IN groups -%]
<li><input type="radio" name="group" value="[% gr.id %]">[% gr.name %]</input></li>
[%- END -%]
我知道p IN小伙伴说的不对但是就是想告诉你我想要达到的目的。对模板代码有什么建议吗?
您需要大量修改代码才能实现这一点。
Perl 散列中的键是 字符串 ,而不是标量。使用任何不是字符串的东西作为散列中的键(例如,表达式 { $gr => $gra }
中的 $gr
将导致它被字符串化,就像您将它插入到字符串中或打印出来一样. 除非您在 My::Model::Group
对象上显式重载了 ""
运算符,否则密钥最终将按照以下行存储为文字字符串:
"My::Model::Group=HASH(0x1234567890)"
此字符串无法转换回原始对象 -- 事实上,原始对象可能在超出范围后立即被垃圾回收,因此它根本不存在。
考虑将该对存储为数组引用,例如
push @$groups, [$gr, $gra];
假设您有一个数组数组,其中每个内部数组包含一对对象。我创建了 Moo 类 来说明。
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' )
],
];
有四对。两个管理员,四个组。其中三个组属于 foo 管理员,一个属于 bar。现在让我们看一下模板。
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 -%]
相关部分显然是 DATA
部分。我们需要将数组数据结构重新组织成一个包含管理员的哈希,然后将每个组排序到一个管理员槽中。
我们不需要创建 by_admin
变量。它将在全局隐式创建。但是我们确实需要为 $by_admin->{$item[0]->name}
设置一个默认值(我现在使用 Perl 语法,以便于理解)。似乎 Template Toolkit 不知道 autovivification, and the DEFAULT
keyword is similar to the //=
assignment operator in Perl.
然后我们可以 push
item
的第一个元素到我们刚刚创建的数组 ref 中(如果它还不存在)在哈希 ref 元素中,键为 item.1.name
里面by_name
.
准备工作完成后,剩下的就是一个简单的循环。我们迭代 by_admin
的 sort
ed keys
,然后迭代该键后面的数组 ref。
这是输出:
bar -> 3
foo -> 1
foo -> 2
foo -> 4
不在模板中而是在您的控制器中进行预处理是有意义的。作为普通的 Perl 代码,它应该更容易阅读。
my %by_admin;
for my $group (@$groups) {
push @{ $by_admin{ $group->[1]{name} } }, $group->[0];
}
请注意,为了简洁起见,我省略了 use strict
和 use warnings
。