Chainedアクションのサンプル
例として、商品アイテムとそのアイテムに対して登録されたコメントを表示/登録/編集/削除するWebアプリケーションを考えます。これらのアクションを定義するためにFCSample::Controller::Item
コントローラを作成します。
$ ./FCSample/script/fcsample_create.pl controller Item
この商品アイテムアプリケーションで必要となるパスとして次のものを定義します。/item
の後にはItemIDが、/comment
の後にはCommentIDが指定されるものとします。
パス | 説明 |
/item | 登録されているアイテムの一覧表示 |
/item/* | 指定されたアイテムの情報を表示 |
/item/*/comment | 指定されたアイテムのコメント一覧を表示 |
/item/*/comment/add | 指定されたアイテムにコメント追加 |
/item/*/comment/* | 指定されたアイテム、コメントを表示 |
/item/*/comment/*/edit | 指定されたアイテム、コメントの編集 |
/item/*/comment/*/delete | 指定されたアイテム、コメントの削除 |
これらのパスに対応させるアクションとして、次のものを定義しました。この中でパスと関連づけられていないアクションとしてcapture_item
とcapture_comment
があります。これらのアクションで、データの取得、Stashへの登録を行うことで、依存する他のアクションではそれぞれの目的に特化した処理を定義できるようになり、コードの再利用性が高まります。
アクション名 | 対応するパス | :Chainedの引数 | :PathPartの引数 | 説明 |
index | /item | × | × | Itemコントローラのindexアクションとしてアイテム一覧を表示 |
capture_item | × | / | 'item' | 引数として受け取ったItemIDからアイテム情報を取得 |
show_item | /item/* | capture_item | '' | 指定されたアイテム情報を表示 |
show_comment_list | /item/*/comment | capture_item | 'comment' | 指定されたアイテムのコメント一覧を表示 |
add_comment | /item/*/comment/add | capture_item | 'comment/add' | 指定されたアイテムにコメント追加 |
capture_comment | × | capture_item | 'comment' | 引数として受け取ったItemIDとCommentIDから該当するコメント情報を取得 |
show_comment | /item/*/comment/* | capture_comment | '' | 指定されたコメントを表示 |
edit_comment | /item/*/comment/*/edit | capture_comment | 'edit' | 指定されたコメントの編集 |
delete_comment | /item/*/comment/*/delete | capture_comment | 'delete' | 指定されたコメントの削除 |
それでは、上記のアクションのコードを見ていきましょう。
package FCSample::Controller::Item; # 省略 # (1)モデルからアイテム一覧を取得し、表示する sub index :Path :Args(0) { my ( $self, $c ) = @_; } # (2)最上位のChainedアクションとして定義し、ItemIDを引数とする sub capture_item :Chained('/') :PathPart('item') :CaptureArgs(1) { my ( $self, $c, $item_id ) = @_; $c->stash->{item_id} = $item_id; } # (3)Stashから取得するItemIDを使用してアイテム情報を表示する sub show_item :Chained('capture_item') :PathPart('') :Args(0) { my ( $self, $c ) = @_; my $item_id = $c->stash->{item_id}; ... } # (4)複数階層のパスをPathPartに指定 sub add_comment :Chained('capture_item') :PathPart('comment/add') :Args(0) { my ( $self, $c ) = @_; my $item_id = $c->stash->{item_id}; if ($c->request->method eq 'POST') { # POSTの場合にはパラメータから受け取ったコメントを登録 $comment = $c->request->parameters->{comment}; ... } else { # POST以外の場合にはコメント登録フォームを表示 ... } ... } # (5)エンドポイントではないアクションを連鎖 sub capture_comment :Chained('capture_item') :PathPart('comment') :CaptureArgs(1) { my ( $self, $c, $comment_id ) = @_; $c->stash->{comment_id} = $comment_id; }
(1)モデルからアイテム一覧を取得し、表示する
index
アクションとして定義し、モデルから取得したアイテム一覧を表示します。
(2)最上位のChainedアクションとして定義し、ItemIDを引数とする
最上位のChained
アクションとして定義するため、:Chained('/')
としています。またこのアクションはエンドポイントではないためCaptureArgs
アトリビュートで引数の数を指定しています。ここではItemID
だけを引数として受け取ります。
この例では、ItemID
をStashに登録していますが、通常はモデルから取得したアイテムデータそのものをStashに登録します。
(3)Stashから取得するItemIDを使用してアイテム情報を表示する
このアクションは、capture_item
アクションの後に呼び出される必要があるため、:Chained('capture_item')
のように指定します。またエンドポイントになるためArgs
アトリビュートで引数の数を指定しています。
必要なアイテム情報はcapture_item
アクションによってStashに登録されているので、ここではアイテムの表示に特化した処理だけを定義できます。
(4)複数階層のパスをPathPartに指定
PathPart
には、ここで指定しているように複数階層のパスを指定することもできます。
また、Chained
アクションに限った方法ではありませんが、add_comment
アクションでは同じURLを指定された場合でも、HTTPのメソッドによって処理を切り替えています。
(5)エンドポイントではないアクションを連鎖
capture_comment
アクションはエンドポイントではありませんが、上位アクションとしてcapture_item
を指定しています。このようにChained
アクションではさまざまなURLに対応できるように、柔軟な定義が可能となっています。
まとめ
本記事では、アクションからURLを遷移することなく別アクションを呼び出す方法、そしてリダイレクトを行う方法を紹介しました。そして、第3回の記事で簡単に紹介したChained
アクションについてもサンプルを交えて説明しました。
次回は、Perlでのオブジェクト指向プログラミングについて簡単に説明し、Catalyst5.8から基盤として使用されているMoose、そしてMooseを使用したモダンなCatalystプログラミングなどについて説明していく予定です。
参考資料
- 『Catalyst-Manual-5.8000』
- 『Catalyst::DispatchType::Chained』
- 『モダンPerl入門』 牧大輔 著、翔泳社、2009年2月