tokuhirom's Blog

Amon2::Setup::Flavor::Large + Teng をもちいた簡単な掲示板の作成 〜 軽量フレームワークAmon2入門 (7) 〜

前回から大分時間が経ってしまいましたが、唐突に Amon2+Teng で大きめのアプリをつくるチュートリアルでもはじめましょう。Teng というのは、O/R Mapper とよばれる種類のライブラリで、DB へのアクセスを簡単にしてくれるものです。

さて、とにかくも準備からはじめましょう。

% cpanm Amon2 Amon2::Setup::Flavor::Teng Teng

で Amon2 をインストールします。

% amon2-setup.pl --flavor=Large,Teng MyBBS

とするとスケルトンができあがりますので

% cd MyBBS

として、プロジェクト用のディレクトリにはいります。

%cpanm --installdeps .

とすると、依存モジュールがインストールされます。

% plackup app.psgi -R lib

とうつと、サーバープロセスが起動します。
http://localhost:5000/ にブラウザでアクセスすると、トップページがみえるようになっています。

とりあえず、簡単な掲示板をつくってみましょう。とっかかりには最適です。

tmpl/pc/index.tt がトップページのテンプレなので、まずこれをごっそり書換えましょう。

こんな風にすればとりあえずよさげですね。上の方にフォームを表示して、下の方に、実際に投稿されたものを表示します。

[% 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>

[% END %]

ではこの掲示板の内容をデータベースに保存しましょう。今回は O/R Mapper である Teng をつかってつくります。データベースには mysql をつかいますので、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)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

最初のテーブルの方は最初からかかれているかとおもいます。

つづいて mysql のデータベースの設定をおこないましょう。

mysqladmin -uroot create mybbs

のようにコマンドラインでうつと mybbs という名前のデータベースが作成されます。パスワードなどを設定している場合には随時設定をおこなってください。

mysql -uroot mybbs < sql/mysql.sql

としてスキーマをつっこんでおきましょう。

データベースへの接続設定は config/development.pl に書きます。このファイルは設定ファイルですが、ただの perl script なので、すきにロジックが書けます。最後にハッシュリファレンスをかえすようにしておけばなにをしてもかまいません。設定ファイルは環境変数によってきまり、 config/$ENV{PLACK_ENV}.pl という名前のファイルです。デフォルトでは development が設定されます。
たとえば以下のように設定してみてください。

+{
    'DBI' => [
        "dbi:mysql:database=mybbs",
        'root',
        '',
        {mysql_enable_utf8 => 1}
    ],
};

さて、ここまでで設定がひととおりおわりましたから、いよいよコードをかいていきましょう。
"lib/MyBBS/PC/C/Entry.pm" というファイルをひらいて、以下のようにかきます。

package MyBBS::PC::C::Entry;
use strict;
use warnings;
use utf8;

sub create {
    my ($class, $c) = @_;
    if (my $body = $c->request->param('body')) {
        $c->db->insert(
            'entry' => {
                body => $body,
            }
        );
    }
    return $c->redirect('/');
}

1;

今回は MyBBS::PC::C::Entry::create というメソッドを定義しましたから、このメソッドは /entry/create という URL にマップされます。こういう風に定義されたメソッドは、Large Flavor をつかっている場合は自動的に URL にマップされるようになっているのです。このあたりの実装については lib/MyBBS/Web/Dispatcher.pm をみてください。また、どのメソッドがどの URL にマップされているかは、plackup を起動したときのメッセージで確認することができます。

では中身をこまかくみていきましょう。$c は前にもでてきましたが、コンテキストオブジェクトです。このオブジェクトは MyBBS::Web のインスタンスであり、MyBBS::Web は MyBBS.pm の子クラスです。このオブジェクトをとおしてだいたいの操作はおこなわれます。
$c->request は HTTP リクエストをあらわすオブジェクトです。HTTP リクエストにかんする情報をあつかうときにつかいます。このメソッドは Amon2::Web::Request のインスタンスをかえします。HTTP リクエストの内容を見たいときによびます。ここでは 'body' というパラメータの値を取得しています。$c->request は頻繁につかわれるメソッドですから、$c->req という省略記法も用意されています。Amon2::Web::Request は Plack::Request の子クラスなので、このクラスで利用可能なメソッドの情報をしりたい場合には Plack::Request のドキュメントをよむとよいでしょう。
$c->db メソッドは、Teng のインスタンスをかえします。これは Amon2 の機能ではなくてlib/MyBBS.pm にベタ書きされているメソッドです。MyBBS ではデータベースにかんする操作は基本的にこれをとおしておこないます。
$c->db->insert メソッドは SQL の INSERT 文を発行するメソッドです。

INSERT INTO entry (body) VALUES ('hogehoge');

のようなクエリが発行されます。

$c->redirect('/') メソッドをよぶと Amon2::Web::Response オブジェクトが生成されます。Amon2 の世界では、コントローラはすべて Amon2::Web::Response(Plack::Response) のインスタンスを return することになっています。リクエストをうけとってレスポンスをかえすだけです。この場合だと

302 Moved Permanently
Location: http://localhost:5000/

のようなレスポンスが生成されます。

さて、これまでで入力はできるようになったので表示してみましょう。lib/MyBBS/Web/C/Root.pm にトップページのコードがはっているので、編集しましょう。

package MyBBS::PC::C::Root;
use strict;
use warnings;
use utf8;

sub index {
    my ($class, $c) = @_;
    my @entries = $c->db->search('entry');
    $c->render('index.tt', { entries => \@entries});
}

1;

$c->db->search() メソッドをよぶことにより entry テーブルの内容を SELECT してきます。その結果をテンプレートエンジンにわたしているというだけです。
$c->render() メソッドをよぶと、HTML をレンダリングして HTTPレスポンスオブジェクトを得ることができます。この場合だと tmpl/pc/index.tt をレンダリングして、その結果をかえしているというわけです。この際、第2引数にハッシュリファレンスをわたすと、テンプレートの側で entries という変数を利用できるようになります。

tmpl/pc/index.tt に以下のように表示のためのコードをいれると、ポストされた結果がテンプレートに表示されるようになります。

[% 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 %]<br />
[% END %]

[% END %]

というわけで、シンプルな掲示板ができましたが、この状態ではまだただの掲示板です。また、機能的にも掲示板としての機能がちょっとたりてないようです。

などなど、かんがえはじめればキリがないでしょう。