Доступ к элементу массива, который является анонимным хешем, в Template Toolkit в 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 не правы, но это показать вам, чего я хочу достичь. Есть предложения по коду шаблона?


person Albert    schedule 03.05.2017    source источник
comment
FOREACH item IN list правильно. Но вы хотите перегруппировать их, верно? Это как сводка в Excel. У вас есть что-то вроде { 1 => 'foo', 2 => 'bar', 3 => 'foo' }, и вы хотите показать, что у пользователя foo есть 1, 3, а у bar есть 2. Я понял?   -  person simbabque    schedule 03.05.2017
comment
Да, это правильно. Вы поняли абсолютно правильно   -  person Albert    schedule 03.05.2017
comment
Там больше одного админа? Я еще не понимаю ожидаемый результат.   -  person simbabque    schedule 03.05.2017
comment
Администратор будет один и тот же, я имею в виду, что он будет одним и тем же администратором для нескольких групп. но я хочу захватить группы и админа в виде хэша. Позже я сделаю что-то еще с админом, но не сейчас. Итак, теперь желаемым результатом будет имя и идентификатор группы.   -  person Albert    schedule 03.05.2017
comment
Итак, вы хотите сгруппировать по администратору? Это логика, которая действительно не должна входить в шаблон. Но это возможно.   -  person simbabque    schedule 03.05.2017
comment
Правильно, а что посоветуете?   -  person Albert    schedule 03.05.2017
comment
Я сейчас борюсь с синтаксисом TT.   -  person simbabque    schedule 03.05.2017


Ответы (2)


duskwuff уже объясняет в своем ответе, что вы не можете использовать объекты в качестве хеш-ключей, поскольку они сериализуются, и вы потеряете предметность. Мой ответ основан на этом.

Допустим, у вас есть массив массивов, где каждый внутренний массив содержит пару объектов. Я создал классы 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 не поддерживает автовивификацию и ключевое слово DEFAULT аналогично //= оператор присваивания в Perl.

Затем мы можем push первый элемент item в массив ref, который мы только что создали (если он еще не существовал) внутри элемента hash ref с ключом item.1.name внутри by_name.

Как только мы закончим подготовку, все остальное будет простым циклом. Мы повторяем sorted keys из by_admin, а затем повторите ссылку на массив, которая стоит за этим ключом.

Вот результат:

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 для краткости.

person simbabque    schedule 03.05.2017
comment
с этим фрагментом кода я понял. Большое спасибо! Вы, ребята, сделали мой день - person Albert; 03.05.2017
comment
@ Альберт, действительно важная часть - это DEFAULT. Вот что мне потребовалось время, чтобы понять. - person simbabque; 03.05.2017
comment
Как вы упомянули, это лучше всего делать на Perl (а не на TT), поэтому для этого я добавил код Perl. - person ikegami; 03.05.2017
comment
Спасибо @ikegami. - person simbabque; 03.05.2017

Вам нужно будет значительно переработать свой код, чтобы сделать это возможным.

Ключи в хэшах Perl — это строки, а не скаляры. Использование чего-либо, что не является строкой, в качестве ключа в хэше (например, $gr в выражении { $gr => $gra } приведет к тому, что оно будет преобразовано в строку, как если бы вы вставили его в строку или напечатали. Если вы явно не перегрузили "" оператор на объекте My::Model::Group, ключ в конечном итоге будет сохранен в виде буквальной строки в строках:

"My::Model::Group=HASH(0x1234567890)"

Эта строка не может быть преобразована обратно в исходный объект — на самом деле исходный объект, вероятно, был удален сборщиком мусора, как только он вышел из области видимости, поэтому он больше не существует.

Вместо этого рассмотрите возможность сохранения пары в качестве ссылки на массив, например.

push @$groups, [$gr, $gra];
person Community    schedule 03.05.2017
comment
хорошо, но как мне получить доступ к идентификатору в $gr в шаблоне? - person Albert; 03.05.2017