スタッフブログ Staff Blog

table要素を用いたデータ出力時のMTMLをプラグインで簡素化する

ある案件でtable要素によるデータ出力を検討していたのですが、非常に冗長なMovable Typeのテンプレートになることが分かりました。具体的には1日のデータを1記事に入力し、以下のように表示する実装です。(現時点では案件の内容をそのまま出すことができないため、実際の内容を改変しています。)

各地の最高気温(過去5日間)
地域 2月20日 2月21日 2月22日 2月23日 2月24日
平均気温 6.38 7.7
札幌 -0.7 0.1      
仙台 5.5 6.2      
東京 9.3 11.0      
大阪 8.7 9.8      
広島 9.1 11.4      

本件ではPowerCMSを採用しており、管理画面では以下のようにスニペットフィールドを利用してデータを入力します。
写真:データ入力画面の様子

明らかになった課題の原因

明らかになった課題の原因は、日々の記事を列方向に出力(展開)していることです。行と列が上記サンプルと逆になり1記事を1行で出すのであればそのままMTEntriesでループを回せば良いのですが、今回の場合は一旦変数に格納する必要がありそうです。サンプルのテンプレートを示します。

<mt:Entries lastn="10" sort_order="ascend">
<$mt:SetVar name="page.temperature_sum" value="0"$>
<mt:SetVarBlock name="page.week" function="push"><$mt:EntryTitle$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_area1" function="push"><$mt:EntryTemperature key="temperature_area1"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_area2" function="push"><$mt:EntryTemperature key="temperature_area2"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_area3" function="push"><$mt:EntryTemperature key="temperature_area3"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_area4" function="push"><$mt:EntryTemperature key="temperature_area4"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_area5" function="push"><$mt:EntryTemperature key="temperature_area5"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_sum" op="+"><$mt:EntryTemperature key="temperature_area1"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_sum" op="+"><$mt:EntryTemperature key="temperature_area2"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_sum" op="+"><$mt:EntryTemperature key="temperature_area3"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_sum" op="+"><$mt:EntryTemperature key="temperature_area4"$></mt:SetVarBlock>
<mt:SetVarBlock name="page.temperature_sum" op="+"><$mt:EntryTemperature key="temperature_area5"$></mt:SetVarBlock>
<mt:Ignore>ここで平均気温を算出し変数に格納。</mt:Ignore>
</mt:Entries>

<h2>気温の状況</h2>
<table>
<caption>各地の最高気温(過去5日間)</caption>
<thead>
<tr>
<th scope="col">地域</th>
<th scope="col"><$mt:Var name="page.week" index="0"$></th>
<th scope="col"><$mt:Var name="page.week" index="1"$></th>
<th scope="col"><$mt:Var name="page.week" index="2"$></th>
<th scope="col"><$mt:Var name="page.week" index="3"$></th>
<th scope="col"><$mt:Var name="page.week" index="4"$></th>
</tr>
</thead>
<tfoot>
<th scope="row">平均気温</th>
<td><$mt:Var name="page.average_temperature" index="0"$></td>
<td><$mt:Var name="page.average_temperature" index="1"$></td>
<td><$mt:Var name="page.average_temperature" index="2"$></td>
<td><$mt:Var name="page.average_temperature" index="3"$></td>
<td><$mt:Var name="page.average_temperature" index="4"$></td>
</tfoot>
<tbody>
<tr>
<th scope="row">札幌</th>
<td><$mt:Var name="page.temperature_area1" index="0"$></td>
<td><$mt:Var name="page.temperature_area1" index="1"$></td>
<td><$mt:Var name="page.temperature_area1" index="2"$></td>
<td><$mt:Var name="page.temperature_area1" index="3"$></td>
<td><$mt:Var name="page.temperature_area1" index="4"$></td>
</tr>
<tr>
<th scope="row">仙台</th>
<td><$mt:Var name="page.temperature_area2" index="0"$></td>
<td><$mt:Var name="page.temperature_area2" index="1"$></td>
<td><$mt:Var name="page.temperature_area2" index="2"$></td>
<td><$mt:Var name="page.temperature_area2" index="3"$></td>
<td><$mt:Var name="page.temperature_area2" index="4"$></td>
</tr>
<tr>
<th scope="row">東京</th>
<td><$mt:Var name="page.temperature_area3" index="0"$></td>
<td><$mt:Var name="page.temperature_area3" index="1"$></td>
<td><$mt:Var name="page.temperature_area3" index="2"$></td>
<td><$mt:Var name="page.temperature_area3" index="3"$></td>
<td><$mt:Var name="page.temperature_area3" index="4"$></td>
</tr>
<tr>
<th scope="row">大阪</th>
<td><$mt:Var name="page.temperature_area4" index="0"$></td>
<td><$mt:Var name="page.temperature_area4" index="1"$></td>
<td><$mt:Var name="page.temperature_area4" index="2"$></td>
<td><$mt:Var name="page.temperature_area4" index="3"$></td>
<td><$mt:Var name="page.temperature_area4" index="4"$></td>
</tr>
<tr>
<th scope="row">広島</th>
<td><$mt:Var name="page.temperature_area5" index="0"$></td>
<td><$mt:Var name="page.temperature_area5" index="1"$></td>
<td><$mt:Var name="page.temperature_area5" index="2"$></td>
<td><$mt:Var name="page.temperature_area5" index="3"$></td>
<td><$mt:Var name="page.temperature_area5" index="4"$></td>
</tr>
</tbody>
</table>

このテーブルだけで完結するのであればそのままでも良いかもしれませんが、今回は同じレイアウトで異なるデータを繰り返し表示しなければならなかったため、テンプレート内が値を変数にセットするコードで埋め尽くされそうに感じました。

私が考えた解決策

テンプレートが冗長になる原因、

  • データを変数にセットするコード
  • 平均気温を算出するためのコード

をプラグイン側で対応できないか、と考えました。

例えば、<$mt:SetArrayFromTable cf_basename="[カスタムフィールドのベースネーム]" lastn="10"$>とすると、記事を10件取得して各データと算出した平均気温を配列に格納した上でMTの変数にセットするものです。

プラグインのサンプルコード

blog_idが固定値になっている、公開記事に限定していないなど、まだ完成形ではありませんが下記のようなプラグインが仕上がりつつあります。MT::Entry->load()で記事をロードしてループを回しているだけの比較的基礎的な内容です。

sub _hdlr_set_array_from_table{
    my ( $ctx, $args ) = @_;
    my @entries = MT::Entry->load( { blog_id => 5 }, { limit => $args->{ lastn } } );
    my $table = {};

    foreach my $entry ( @entries ){
        my $meta = &get_meta($entry);
        my $weekstart = $meta->{ 'entry_week_start_date' };
        my $snippet_data = $meta->{ $args->{ cf_basename } };
        my $subtotal = 0;

        # 週を格納
        _push_data( \$table, 'week', $entry->title );

        # キー毎にデータを格納する
        # 同時に平均気温を算出するための気温の合計を計算する
        while ( my ($key, $val) = each %{$snippet_data} ) {
            _push_data( \$table, $key, $val );

            $subtotal += $val;
        }

        # 平均気温を格納
        # 実際の案件では平均気温ではなく値の小計なので下記のようなコードになっています
        _push_data( \$table, 'subtotal', $subtotal );
    }

    # MTの変数に割り当て
    for my $key ( keys %$table ) {
        $ctx->stash( 'vars' )->{ $args->{ var_basename } . $key } = \@{$table->{ $key }};
    }

    return '';
}

sub _push_data{
    my ($table, $key, $val) = @_;

    if ( exists( $$table->{ $key } ) ) {
        push @{$$table->{ $key }}, $val;
    } else {
        $$table->{ $key } = ( );
        push @{$$table->{ $key }}, $val;
    }

    return 1;
}

スニペットフィールドのname属性値をハッシュのキーにして、データを配列で格納する方法が分からなかったのですが、MTに詳しい方に下記のようなコードを教えて頂きました。ありがとうございました。

my $table = {};

if ( exists( $table->{ $key } ) ) {
    push @{$table->{ $key }}, $val;
} else {
    $table->{ $key } = ( );
    push @{$table->{ $key }}, $val;
}

まとめ

案件固有のプラグインにはなりますが、比較的簡単なプラグインで課題が解決できました。今回の案件では変数の設定に関わる100行前後のコードが省略できそうです。この後簡単なドキュメントも書いて、メンテナンスがきちんと行える状況にする予定です。

データを今回の例のように出力する事例はよくあるのではないかと思います。参考になりましたら幸いです。

お問い合わせ Contact

制作のご依頼、ご相談などは下記のフォームからご連絡ください。