tokuhirom's Blog

Amon2::Setup::Flavor::Large + Teng をもちいた簡単な掲示板の作成 その3『Tengの行オブジェクトを拡張して投稿日付を表示しよう』 〜 軽量フレームワークAmon2入門 (9) 〜

さて、ページングもついて、一応うごいているといえる状況になっている掲示板ですが、とにもかくにも投稿日付がわからないのは不便といっていいでしょう。

通常ならばもちろん投稿日付を保存するようには設計段階からしておくところですが、今回はチュートリアルなので一度におぼえることをすくなくするために細切れにするとともに、漸近的にウェブアプリケーションを成長させていくための技法をまなんでいっていただこうという気持なわけです。

さて、スキーマにデータを追加するには、まず sql/mysql.sql を更新しましょう。

CREATE TABLE IF NOT EXISTS sessions (
    id           CHAR(72) PRIMARY KEY,
    session_data TEXT
);
CREATE TABLE IF NOT EXISTS entry (
    id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
    body VARCHAR(255) NOT NULL,
    ctime INT UNSIGNED NOT NULL -- ← NEW!
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ctime というカラムを追加してみました。ctime というのは created time 的なかんじです。INTEGER 型にして、epoch time をいれることにします。なんだかんだ DATETIME 型とかつかうとなんかめんどくさい気がするからです。

既存のデータベースのスキーマも更新しなくてはいけませんから、mysql shell で以下のようにうちこんでおきましょう。

ALTER TABLE entry ADD ctime INT UNSIGNED NOT NULL AFTER body;

さて ctime の更新を設定するようにしましょう。sql/mysql.sql に以下のトリガーを追記しつつ、mysql shell からうちこんでみましょう。mysql の trigger の詳細については本稿の対象からはずれますので、こちらを参照してください http://dev.mysql.com/doc/refman/5.1/en/create-trigger.html

DELIMITER |
CREATE TRIGGER before_insert_entry BEFORE INSERT ON entry FOR EACH ROW
BEGIN
  SET NEW.ctime = UNIX_TIMESTAMP(NOW());
END;
|
DELIMITER ;

これで ctime がが自動的に挿入されるようになったはずですから、ウェブアプリケーションの方から入力して、SELECT * FROM entry; などとタイプすれば、ctime に投稿時刻が保存されているのがわかるかとおもいます。

次に、これを実際に表示してみましょう。しかし、これを表示すること自体は簡単なのですが、epoch time をたんに表示されてもユーザーには理解できません。ですから普通の日付表記に変更することが必要です。
ここでは軽量な日時オブジェクトである Time::Piece をつかってみましょう。
lib/MyBBS/PC/C/Root.pm に以下のようにデバッグ文をしこんで確認してみると、$entries->[0] あたりは MyBBS::DB::Row のインスタンスであることがわかります。

sub index {
    my ($class, $c) = @_;
    my $page = $c->req->param('page') || 1;
    my ($entries, $pager) = $c->db->search_with_pager('entry' => {}, {order_by => 'id DESC', page => $page, rows => 20});
    warn ref($entries->[0]); # ←
    $c->render('index.tt', { entries => $entries, pager => $pager });
}

実は MyBBS::DB::Row のクラスは Teng::Schema::Loader によって動的に定義されています。ですが、このクラスにメソッドをはやすこともできるのです。

mkdir lib/MyBBS/DB/Row/

してから lib/MyBBS/DB/Row/Entry.pm をおきます。

package MyBBS::DB::Row::Entry;
use strict;
use warnings;
use utf8;
use parent qw(Teng::Row);
use Time::Piece;

sub ctime_obj {
    my $self = shift;
    return Time::Piece->new($self->ctime);
}

1;

Teng::Schema::Table がなんとなくよみこんでくれるのでいいかんじによみこまれます。

で、よみこまれたら、$entry から $entry->ctime_obj というメソッドがよべるようになるから tmpl/pc/index.tt をこんな風に変更すればいい。ctime_obj からよべる strftime メソッドにかんしては https://metacpan.org/release/Time-Piece こちらをご欄ください。

[% WRAPPER 'include/layout.tt' %]

<form method="post" action="/entry/create">
    <fieldset>
        <div><textarea name="body"></textarea></div>
        <div><input type="submit" value="Post New Entry" class="btn primary" /></div>
    </fieldset>
</form>

[% FOR entry IN entries %]
    [% entry.id %]. [% entry.body %] [% entry.ctime_obj.strftime('%Y-%m-%d(%a) %H:%M') %]<br />
[% END %]

[% INCLUDE 'include/pager.tt' WITH pager=pager %]

[% END %]

というわけで今回は、Teng の行オブジェクトを拡張する方法についてまなびました。