CatalystでURL最適化
Catalyst::DispatchType::Chainedを使うとスッキリしたURLで組める。
似たような事はRegexやLocalRegexでも出来るけどこっちの方が重複するようなコードが軽減できる。 Chained属性に指定したPrivate Names(コントローラーのメソッド名)を使って処理をつなげる事ができる。
例えば以下のURLを処理するとき、処理はこうなる。
http://example.com/users/1/books/2
- /users/1は共通してユーザの検索
- その後に続くbooks/2はそのユーザのIDが2の本を表示
基本
では早速、以下の様なURL群を設計
- /add
- /*
- /*/edit
- /*/entries/*
- /*/entries/*/delete
CatalystのControllerの実装はこんな風になる。
# [CAPTURE] / sub root_cap : Chained('/') PathPart('') CaptureArgs(0) { my ( $self, $c ) = @_; $c->log->debug("[CAPTURE]root_cap"); } # [CAPTURE] /* sub index_cap : Chained('root_cap') PathPart('') CaptureArgs(1) { my ( $self, $c, $member_id ) = @_; $c->log->debug("[CAPTURE]index_cap $member_id"); } # [CAPTURE] /*/entries/* sub entries_index_cap : Chained('index_cap') PathPart('entries') CaptureArgs(1) { my ( $self, $c, $entry_id ) = @_; $c->log->debug("[CAPTURE]entries_index_cap $entry_id"); } # /add sub add : Chained('root_cap') PathPart('add') Args(0) { my ( $self, $c ) = @_; $c->log->debug("add"); } # /* sub show : Chained('index_cap') PathPart('') Args(0) { my ( $self, $c ) = @_; $c->log->debug("index"); } # /*/edit sub edit : Chained('index_cap') PathPart('edit') Args(0) { my ( $self, $c, $alias_id ) = @_; $c->log->debug("edit"); } # /*/entries/* sub entries_index : Chained('entries_index_cap') PathPart('') Args(0) { my ( $self, $c ) = @_; $c->log->debug("entries_index"); } # /*/entries/*/delete sub entries_delete : Chained('entries_index_cap') PathPart('delete') Args(0) { my ( $self, $c ) = @_; $c->log->debug("entries_delete"); }
これを実装してCatalystを起動すると、以下のようなログがでる。
[debug] Loaded Chained actions: .-------------------------------------+--------------------------------------. | Path Spec | Private | +-------------------------------------+--------------------------------------+ | /add | /members/root_cap (0) | | | => /members/add | | /*/edit | /members/root_cap (0) | | | -> /members/index_cap (1) | | | => /members/edit | | /*/entries/*/delete | /members/root_cap (0) | | | -> /members/index_cap (1) | | | -> /members/entries_index_cap (1) | | | => /members/entries_delete | | /*/entries/* | /members/root_cap (0) | | | -> /members/index_cap (1) | | | -> /members/entries_index_cap (1) | | | => /members/entries_index | | /* | /members/root_cap (0) | | | -> /members/index_cap (1) | | | => /members/index | '-------------------------------------+--------------------------------------'
試しに以下のURLにアクセスすると、連続的にchainedしたPrivate Namesが実行されるのが確認できる。
[debug] [CAPTURE]root_cap [debug] [CAPTURE]index_cap 1 [debug] [CAPTURE]entries_index_cap 2 [debug] entries_delete .----------------------------------------------------------------+-----------. | Action | Time | +----------------------------------------------------------------+-----------+ | /auto | 0.000981s | | /members/root_cap | 0.000323s | | /members/index_cap | 0.000166s | | /members/entries_index_cap | 0.000211s | | /members/entries_delete | 0.000135s | | /end | 0.010976s | | -> Example::View::TT->process | 0.005886s | '----------------------------------------------------------------+-----------'
属性の説明
- Chained
- Private Nameを指定することで前処理を指定、あくまでPrivate Nameなので/another/methodのように別のパッケージのPrivate Nameも指定できる。
- PathPart
- 当てはめるパス
- CaptureArgs
- 指定するとキャプチャして、パス後方の指定された数を引数として取得
- Args
- 実際のパスにマッチしたときの処理、パス後方の指定された数を引数として取得
大きく分けて2種類
Chainedを利用したURLの指定には大きく分けて2種類。属性CaptureArgsとArgsによって分けられる。
- CaptureArgs
- を指定するとキャプチャして処理
- Args
- は実際のURLにマッチしたときの処理
例えば、/addを実装してみると以下のコードになる。このとき/を指定してもroot_capは実行されない。
# [CAPTURE] / sub root_cap : Chained('/') PathPart('') CaptureArgs(0) { my ( $self, $c ) = @_; $c->log->debug("[CAPTURE]root_cap"); } # /add sub add : Chained('root_cap') PathPart('add') Args(0) { my ( $self, $c ) = @_; $c->log->debug("add"); }
引数は付ける方が良い
属性のCaptureArgsとArgsは引数を指定した方が良い、指定しないと前方一致のような動きをしていまう。
試しに、Argsの引数をなくすと
# /*/entries/*/delete sub entries_delete : Chained('entries_index_cap') PathPart('delete') Args() { my ( $self, $c ) = @_; $c->log->debug("entries_delete"); }
起動時のPath Specの後方に「...」が付いてしまう。
.-------------------------------------+--------------------------------------. | Path Spec | Private | +-------------------------------------+--------------------------------------+ | /*/entries/*/delete/... | /members/root_cap (0) | | | -> /members/index_cap (1) | | | -> /members/entries_index_cap (1) | | | => /members/entries_delete |
後方が何でも一致する意味も無いと思うので、なるべく引数は指定した方がよさげ。