CakePHPからCatalystに置き替え

メソッドとかサブルーチンとか

訳あって現在作ってたソースを
CakePHP(PHP)からCatalyst(Perl)へ移行中なのです。

文法的にはPHPPerl似てるところもありますが、概念とか呼び方が全然違うので戸惑いの連続です。
http://www15.plala.or.jp/h-kihara-home/prog/perl/ooperl.html

-------------------------------------------
PHP                        ->   Perl
-------------------------------------------
クラス                         パッケージ
オブジェクト(インスタンス)    データ構造
メソッド                       サブルーチン
-------------------------------------------

コントローラーでサブルーチンの呼び出し

ようやく先週 Catalyst::Model::DBIC::Schemaでデータベースとの連携が取れて安心したのもつかの間。
コントローラー側で色々と詰まってました。。

CakePHPでは$this->method();で呼び出していた内容を
Catalystに移行する場合、ちょっと戸惑いました。

例)CakePHP
<?php 
class HogeController extends AppController{
	function test(){
		$this->test2();
	}

	private function test2(){
		echo 'Hello!';
	}
}
?>

上のようなメソッド間でやり取りするようなコードを設計する場合、Catalystには
forward,detach,visit,goと複数の手段があるのでどれを使うかで悩みました。
一旦下記に整理してみました。

forward[他のアクションに一部の処理をゆだねる]
sub forward_from :Local{
	my($self,$c)=@_;
	
	my $result = $c->forward('/ctrl/flow/forward_to',[2,4]);
	#または・・
	my $result = $c->forward('Catalll::Controller::Ctrl::Flow','forward_to',[2,4]);
	
	$c->response->body('AAA:'.$result->[0].'/BBB:'.$result->[1]);
}

sub forward_to :Private{
	my($self,$c,$base,$height)=@_;
	return [$base * 2,$height * 3];
}
detach[他のアクションに処理を引き渡す(つまり呼び出し元に処理を戻さない)]
$c->detach('/hello/intro',['hoge','foo']);
$c->detach('Catal::Controller::Hello','intro');
$c->detach();
visit[転送先から処理を返す(転送先の処理をあたかも新規のリクエストであるかのように振舞う)]
sub visit_from :Local{
  my($self,$c)=@_;
  $c->visit('/ctrl/flow/visit_to');
}

sub visit_to :Private{
  my($self,$c)=@_;
  $c->response->body('正常動作中');
}

sub begin :Private{
}
go[転送先から処理を返さない(転送先の処理をあたかも新規のリクエストであるかのように振舞う)]

パターン見てみると、要は返り値があるかないか、新規のリクエストとして処理するか
という違いがあるようでした。
今回の処理についてはふつうにforwardで実装しようと思っています。

連鎖アクションの書き方

さて一緒に覚えた事として、画面遷移について
function top(){echo 'トップ';} -> function confirm(){echo '確認画面';} -> function execute(){echo '完了画面';}
という3連パターンがよくあるわけですが、これについてCatalystでは「連鎖アクション」で実装するそうです。
http://blog.livedoor.jp/sasata299/archives/51242968.html
試しにCakePHPで連鎖アクションと検索してみたのですが出てこなかったので、概念としては無いのでしょうか。
下記のような感じで連鎖アクションを記述することが可能です。

sub chain_top :Chained('/') :PathPart('first') :CaptureArgs(0) {
	my($self,$c)=_;
	$c->stash->{body}='TOPアクション';
}

sub chain_second :Chained('chain_top') :PathPart('second') :CaptureArgs(1){
	my($self,$c,$id)=_;
	$c->stash->{body} .= 'SECONDアクション:'.${id};
}

sub chain_third :Chained('chain_second') :PathPart('third'){
	my($self,$c)=_;
	$c->stash->{body} .= 'THIRDアクション';
	$c->response->body($c->stash->{body});
}

呼び出し->
http://aaaaa:3000/first/second/108/third

属性の指定方法

http://d.hatena.ne.jp/Climber/20081007/1223349565
が参考になりました。

PathPart属性.....
Args属性.....エンドポイントで引数を制限する
PathPrefix属性.....

連鎖アクションを使うことで入力チェックなどができます
#memoを新規作成するためのアクション
sub note_create :Chained('note_base') :PathPart('create') :Args(0){
  my($self,$c)=@_;
  if($c->req->method eq 'POST'){
  	$c->forward('note_validate');
  	if(scalar@{$c->stash->{errors}}){$c->go('note_error');}
  }
}

sub note_edit :Chained('note_details') :PathPart('edit') :Args(0){
	my($self,$c)=@_;
	if($c->req->method eq 'POST'){
		$c->forward('note_validate');
		if(scalar@{$c->stash->{errors}}){$c->go('note_error');}
	}
}

#検証用のプライベートアクション
sub note_validate :Private{
  my($self,$c)=@_;
  my $p = $c->request->body_params;
  my @errs;
  
  push @errs,'件名が空です。' unless $p->{title};
  push @errs,'本文が空です。' unless $p->{body};
  $c->stash->{erros} = \@errs;
}

#エラー発生時に呼び出さnote_error.ttでエラーを描画
sub note_error :Private{}

-------
<html>
[% FOREACH item = errors %]
<li>[% item %]</li>
[% END %]
-------

Catalystデバッグの仕方

PHPだとvar_dump();で済んだ所、catalyst(Perl)だと
ちょっと面倒っぽい。

スカラー
use Data::Dumper;
my $room_no = 1;
$c->log->debug($room_no);
---------
[debug]1
---------
ハッシュ
use Data::Dumper;
my %hash = (x=>1111,y=>222);
$c->log->debug(\%hash);
$c->log->debug(%hash);
$c->log->debug($hash{x});
---------
[debug]1
---------
[debug] HASH(0x195451d8)
[debug]y
222
x
1111
[debug] 1111


そんなわけで引き続き頑張ります。