Docurain Labo

Docurainサービス開発日記

明細行を出力する際に利用する配列操作関数について

帳票はヘッダー、明細そしてフッターで構成されています。ヘッダーやフッターは各帳票で一度しか出力しないので簡単ですが、明細行は次のような機能が必要でしょう。

  1. 明細データから指定した行数分のデータを取り出す
  2. 明細データを指定した行数ごとに分割する

この記事では帳票を作る際に必要な配列操作について解説します。

今回利用するデータについて

今回利用するデータは次のような内容であることとします。

{
    "items": [
        {"name": "商品1", "price": 100, "unit": 10},
        {"name": "商品2", "price": 200, "unit": 20},
        {"name": "商品3", "price": 300, "unit": 30},
        {"name": "商品4", "price": 400, "unit": 40},
        {"name": "商品5", "price": 500, "unit": 50},
        {"name": "商品6", "price": 600, "unit": 60},
        {"name": "商品7", "price": 700, "unit": 70},
        {"name": "商品8", "price": 800, "unit": 80},
        {"name": "商品9", "price": 900, "unit": 90},
        {"name": "商品10", "price": 1000, "unit": 100},
        {"name": "商品11", "price": 1100, "unit": 110},
        {"name": "商品12", "price": 1200, "unit": 120},
        {"name": "商品13", "price": 1300, "unit": 130},
        {"name": "商品14", "price": 1400, "unit": 140}
    ]
}

この items キーが明細データになります。

1ページ4明細の帳票の場合

今回のデータは明細が14件ありますので、1ページ4明細の場合は次のような帳票になります。

  • 全4ページ
  • 4ページ目が2件

こうした帳票を作る場合は、まず items キーの内容を次のように分割すれば良さそうです。

[
    [
            {"name": "商品1", "price": 100, "unit": 10},
            {"name": "商品2", "price": 200, "unit": 20},
            {"name": "商品3", "price": 300, "unit": 30},
            {"name": "商品4", "price": 400, "unit": 40}
    ],
    [
            {"name": "商品5", "price": 500, "unit": 50},
            {"name": "商品6", "price": 600, "unit": 60},
            {"name": "商品7", "price": 700, "unit": 70},
            {"name": "商品8", "price": 800, "unit": 80}
    ],
    [
            {"name": "商品9", "price": 900, "unit": 90},
            {"name": "商品10", "price": 1000, "unit": 100},
            {"name": "商品11", "price": 1100, "unit": 110},
            {"name": "商品12", "price": 1200, "unit": 120}
    ],
    [
            {"name": "商品13", "price": 1300, "unit": 130},
            {"name": "商品14", "price": 1400, "unit": 140}
    ]
]

このようにデータを分割するメソッドが chunk です。次のように実行します。

#set($pages = $ROOT.items.chunk(4))

この chunk メソッドは指定したデータ件数(今回は4)ごとにデータを分割して、配列にします。つまり $pages の内容は次のようになります。

[
    [
            {"name": "商品1", "price": 100, "unit": 10},
            {"name": "商品2", "price": 200, "unit": 20},
            {"name": "商品3", "price": 300, "unit": 30},
            {"name": "商品4", "price": 400, "unit": 40}
    ],
    [
            {"name": "商品5", "price": 500, "unit": 50},
            {"name": "商品6", "price": 600, "unit": 60},
            {"name": "商品7", "price": 700, "unit": 70},
            {"name": "商品8", "price": 800, "unit": 80}
    ],
    [
            {"name": "商品9", "price": 900, "unit": 90},
            {"name": "商品10", "price": 1000, "unit": 100},
            {"name": "商品11", "price": 1100, "unit": 110},
            {"name": "商品12", "price": 1200, "unit": 120}
    ],
    [
            {"name": "商品13", "price": 1300, "unit": 130},
            {"name": "商品14", "price": 1400, "unit": 140}
    ]
]

後はこの pages ごとにループ処理をすれば、ページごとの明細を出力できます。

#for ($page in $pages)  ## ページ単位のループ
  #for ($item in $page) ## 明細ごとのループ
    #end
#end

1ページ目が4件の明細、2ページ目以降は6件という帳票の場合

次は1ページ目を4件、2ページ目以降は6件の場合は、次のような帳票になります。明細データは先ほどと同じく14件とします。

  • 全3ページ
  • 1ページ目は4件、2ページ目は6件、3ページ目は4件

1ページ目用のデータを取得する

まず items キーの中から4件のデータを取得します。この時には take を使います。

#set($firstPage = $ROOT.items.take(4))

この時、$firstPage は次のような内容になっています。

[
        {"name": "商品1", "price": 100, "unit": 10},
        {"name": "商品2", "price": 200, "unit": 20},
        {"name": "商品3", "price": 300, "unit": 30},
        {"name": "商品4", "price": 400, "unit": 40}
]

1ページ目のテンプレートを作り、そのまま出力を行います。

2ページ目以降のデータを取得する

次に2ページ目以降、データを6件ずつに分割します。この時に使うのが drop です。 drop は指定した数字(今回は4)分、データをスキップします。そして残りのデータについて chunk で6件ずつに分割します。

#set($pages = $ROOT.items.drop(4).chunk(6))

この時の $pages の内容は次のようになります。

[
    [
            {"name": "商品5", "price": 500, "unit": 50},
            {"name": "商品6", "price": 600, "unit": 60},
            {"name": "商品7", "price": 700, "unit": 70},
            {"name": "商品8", "price": 800, "unit": 80},
            {"name": "商品9", "price": 900, "unit": 90},
            {"name": "商品10", "price": 1000, "unit": 100},
 ],
    [
            {"name": "商品11", "price": 1100, "unit": 110},
            {"name": "商品12", "price": 1200, "unit": 120},
            {"name": "商品13", "price": 1300, "unit": 130},
            {"name": "商品14", "price": 1400, "unit": 140}
    ]
]

2ページ目以降のテンプレートを作り、出力します。

最後のページだけ出力内容が異なる場合

帳票において、最後のページだけ出力内容が異なるケースは良くあります。たとえば集計行の出力です。ループ処理の中で最後のページかどうか判別する際には $foreach.last を使います。

#for ($page in $pages)  ## ページ単位のループ
  #for ($item in $page) ## 明細ごとのループ
    #end
  #if ($foreach.last) ## 最後のページの場合
      ## 集計行の表示など
    #end
#end

$foreach.last を含め、次のようなデータがあります。

データ 意味
$foreach.first 真偽値 ループの先頭ならtrue
$foreach.last 真偽値 ループの最後ならtrue
$foreach.count 数値 ループの回数(1..)
$foreach.index 数値 ループのインデックス(0..)

なお、印刷範囲を3つにすることで「最初のページ」「2ページ目以降のページ」「最後のページ」に分けて印刷範囲を作成することもできます。この時、明細データの取得方法は次のようになります。 firstNum は最初のページの明細件数、 num は2ページ目以降の明細件数です。

  • 最初のページ
    #set($page = $e.items.take(firstNum))
  • 2ページ目以降のページ(最後のページを除く)
    #set($pages = $e.items.drop(firstNum).chunk(num).slice(0, -1))
  • 最後のページ
    #set($page = $e.items.drop(firstNum).chunk(num).slice(-1)[0])

この3つのパターンを使うことで、 $foreach を使わない形でも帳票を作成できます。

デバッグ

もし明細の内容がどのようになっているか気になった場合には B列 以降の列で %{page} のように指定した上で帳票出力してください。データの内容がダンプされるので、どういった項目があるのか確認できます。

まとめ

帳票で明細行を出力する処理は改ページにも繋がるので、処理が複雑になります。頭の中で出力結果を考えながら記述するのは難しいですが、慣れてしまえばすぐできるようになるでしょう。ぜひトライしてください。