質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.72%

perlでDBIとTemplate Toolkit

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 1,010

taromame

score 11

前提


前回の質問同様、リファクタリング中の社内業務システム(LAPP) の件になります。

別の業務システムで使ってる「PHPxSmarty」みたいにすらすらいけるかな、と学習機会のあったテンプレートエンジン「Template Toolkit」を採用しましたが、二日間ほどサンプルコードとネットのにらめっこの末力つき、、投稿いたします。

実現したいこと


まずはサンプルコードを元にDBからのリスト取得と表示にトライしてみました。

テンプレートで単純に置換したいだけの変数は表示されますが
FOREACH での一覧 が表示されません。

取得内容はprint文で表示確認できているので
テンプレートへの渡し方、またはテンプレートの参照が間違っているのかな、と思います。

my ($row) = @$ary_ref;

が何をいれているのかも良くわからず、今更ながら力不足を実感中です。

実際にこれから何十と作り直す機能は、複数のクエリの結果をごにょごにょして画面出力するものばかりなので
こんな事もできずにどうするんだよ~、と気ばかり焦っています。

初歩もいいところですが、どなたかご教授ください。
よろしくお願いいたします。

[CGI側] --------------------------------------------------------------------------
:
# 接続
&db_con;

# ステートメントハンドラの作成
our $sth = $dbh->prepare("SELECT * FROM staff;");

# 実行
$sth->execute() || die "Cannot execute statement : $sth->errstr\n";

# 取得データの処理
while (my $ary_ref = $sth->fetchrow_arrayref) {
    my ($row) = @$ary_ref;
    print "$ary_ref->[0], $ary_ref->[1]\n";  # テストプリントはOK
}

# 開放/切断
&db_discon;

# テンプレートに変数をアサイン
my $vars = {
    system => "$SYSTEM",
    sname => "$sname",
    cmpmsg => "$cmpmsg",
    errmsg => "$errmsg",
 row => "$row",
};

$tt->process('template/msg.html', $vars) || die "Template process failed : $tt->error\n";

[TEMPLATE側] --------------------------------------------------------------------

[% INCLUDE 'template/header.inc' %]

[% IF errmsg %]
<!-- 異常終了 -->
<p><input type="button" class="btn btn-default btn-lg" value="戻る" onClick="history.back()"></p>
<div class="panel panel-danger">
    <div class="panel-heading"> [% sname %]</div>    <div class="panel-body"><span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> [% errmsg %]</div>
</div>

[% ELSIF cmpmsg %]
<!-- 正常終了 -->
<p><input type="button" class="btn btn-default btn-lg" value="戻る" onClick="history.back()"></p>
<div class="panel panel-success">
    <div class="panel-heading"> [% sname %]</div>
    <div class="panel-body"><span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> [% cmpmsg %]</div>
</div>

[% ELSE %]
<!-- 出力テスト -->
<table class="table table-bordered">
<tr><th>社員コード</th><th>名前</th></tr>
[% FOREACH res IN row %]
<tr><td>[% res.0 %]</td><td>[% res.1 %]</td></tr>
[% END %]
</table>

[% END %]

[% INCLUDE 'template/footer.inc' %]


  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

$varsrowのところは、配列の参照の配列をセットします。
名前もrowsにした方が分かりやすいと思います。


まず、Perlでは、左辺をカッコつきにして右辺の配列を代入すると、配列の要素を先頭から順にスカラー変数に格納できます。
下記のように書けば意味がわかるでしょうか。
my ($member_cd, $member_name) = @ary;

また、@$ary_refは、配列のデリファレンスです。配列のリファレンスを配列に変換する処理です。
つまり、

my ($row) = @$ary_ref;

は、DBから取得した行の先頭の要素を$rowに格納しています。
ただ、これでは最後の行の社員コードだけが毎回上書きされてしまい、結果として最後の行の社員コードだけがセットされます。



ここは、社員コードと社員名の配列の参照[ $ary_ref->[0], $ary_ref->[1] ]を、行ごとに配列@rowsに追加していきます。

my @rows = ();
while (my $ary_ref = $sth->fetchrow_arrayref) {
    push @rows, [ $ary_ref->[0], $ary_ref->[1] ];
}

テンプレートに渡す値は、@rowsを参照として渡します。

my $vars = {
    system => "$SYSTEM",
    sname => "$sname",
    cmpmsg => "$cmpmsg",
    errmsg => "$errmsg",
    rows => \@rows,
};


テンプレートは、変数名だけ変えていますが、ほとんど同じです。

<table class="table table-bordered">
<tr><th>社員コード</th><th>名前</th></tr>
[% FOREACH row IN rows %]
<tr><td>[% row.0 %]</td><td>[% row.1 %]</td></tr>
[% END %]
</table>

[% END %]


これで社員一覧が表示できるはずです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/05 14:17

    argius様

    金曜にアンサーいただいのたは気づいてましたが、休み明けの確認でお礼が遅れました。
    ご指摘のコードに変更したら無事一覧表示されました。
    有難うございます!

    が、しかし、、これで解決ではないんです。
    繰り返しとなりますが、実際はカラム数がもっと多い複数のテーブルに対して
    SELECT * FROM の応酬 なので

    SELECTの数だけ配列を用意して、カラムの数だけ
    push @rows, [ $ary_ref->[0], $ary_ref->[1], $ary_ref->[2], $ary_ref->[3],.......];
    と書くのは非効率&可読性も悪く、bind_columnsを使う方がテンプレート側でも解りやすそうだな、、と感じます。
    (HTMLコーダーなどおらず、保守も運用も開発も全部一人です)

    解ってないくせに生意気なんですけど、リファクタリングするならスマートなコードを書きたいな、思っていまして。。
    もう少しDBIのドキュメントに食い下がってみようと思います。

    あわよくば、、(聞くは一時の恥、と開き直り)
    ・複数のSELECT結果(戻りは複数カラムx大量レコード)をtemplateにスマートに渡す書き方
    ・template Toolkit側の書き方をご教授いただけませんでしょうか!

    補足)
    ユーザの要求も対応し尽してから数年安定稼動している業務システムなので、リーダー企業のルール改正がないかぎりデータベースの仕様変更はありません。

    キャンセル

0

冷静になってみると「複数のSELECT結果(戻りは複数カラムx大量レコード)をtemplateにスマートに渡す書き方」を知ってテンプレート側でどうするつもりだったんだ?
テンプレート側にロジックが散らばったら全然スマートじゃないですよね。
愚問でした。

今までどおり、複数のクエリの結果をプログラム側でごにょごにょ処理して出力したい値のみを、argius様に教えていただいた書き方でテンプレートに渡し FOREACH でまわします。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/06 15:50

    返答が遅れてすみません。
    少なくとも、
    push @rows, [ $ary_ref->[0], $ary_ref->[1] ];
    の部分は汎用的に、全部の列をコピーすることは可能だと思います。
    ここでは元のロジックに倣って0,1だけ取り出すようにしていました。

    あと、rowsはrows_of_shain,rows_of_xxxのように複数持たせるのは有りだと思います。必要なものを$varsに設定すればOKです。

    キャンセル

  • 2015/10/06 17:07

    追記ありがとうございます。
    複数配列を渡した場合はテンプレート側のFOREACHは入れ子にすれば良いのですよね。

    SELECT文を見直してみると、「何だよサブクエリー使えばクエリ発行一回ですむじゃん」というケースもあるので、臨機応変に使い分けてみようと思います。
    またすぐ行き詰ると思うので今後ともよろしくお願いします。取り急ぎお礼まで★

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.72%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる