FuelPHPのプレゼンタ(Presenter)について使い方を調べてみました.
プレゼンタとは
MVC(Model-View-Controller)モデルの派生であるMVP(Model-View-Presenter)モデルのPresenterのような機能です.
MVPモデルそのものについては, こちらの記事が参考になりました.
もう少し具体的に, FuelPHPのマニュアルから, FuelPHPが言うところのプレゼンタの役割を抜粋してみました.
- ビューに関するロジックを抽象化してコントローラから切り離すために使う
- データの操作はすべきでない
- データベース他の呼び出しを含める
- ビューの生成に必要なデータを準備する操作を含める
FuelPHPのプレゼンタについては, こちらの記事が参考になりました.
Presenterクラスはバージョン1.7.2でViewModelクラスから改名された経緯があります.
プレゼンタを使う
サンプルとして, 蔵書システムの出版社情報を管理する機能を考えます.
出版社情報を登録する入力フォームを表示するアクションメソッドを, Viewクラスを使って記述すると, このようになります.
class Controller_Publisher extends Controller
{
public function get_create()
{
$form = Fieldset::forge('publisher')->add_model(Model_Publisher::forge());
$form->field('name')->add_label(_(publisher.name));
$form->add('submit', '', array(
'type' => 'submit',
'class' => 'btn btn-primary submit',
'value' => 'Create',
));
$form->populate(Model_Publisher::forge());
return Response::forge(
View::forge('publisher/create')
->set('title', 'Publishers')
->set_safe('form', $form->build())
);
}
}
これをPresenterクラスを使って記述するように変更してみます.
プレゼンタクラスを作成する
Controller_Publisher::get_create()
の中身は入力フォーム生成のための処理なので, その処理をごっそりプレゼンタへ移します. 値をビューに渡す処理のみ調整します.
class Presenter_Publisher_Create extends Presenter
{
public function view()
{
$form = Fieldset::forge('publisher')->add_model(Model_Publisher::forge());
$form->field('name')->add_label(_(publisher.name));
$form->add('submit', '', array(
'type' => 'submit',
'class' => 'btn btn-primary submit',
'value' => 'Create',
));
$form->populate(Model_Publisher::forge());
$this->set('title', 'Publishers');
$this->set_safe('form', $form->build());
}
}
コントローラでPresenterオブジェクトを生成する
コントローラでは, Viewオブジェクトの代わりにPresenterオブジェクトを生成します. Presenter::forge()
の第2引数にはプレゼンタのメソッド名を指定します. 未指定の場合は view
を指定することになります.
class Controller_Publisher extends Controller
{
public function get_create()
{
return Response::forge(
Presenter::forge('publisher/create')
);
}
}
コントローラが随分すっきりしました.
コントローラからプレゼンタへ値を渡したい...?
POSTされたidでfindした情報を表示したい, みたいな場合です.
出版社情報を表示するアクションメソッドを, Viewクラスを使って記述すると, このようになります.
class Controller_Publisher extends Controller
{
public function action_view($id = null)
{
is_null($id) and Response::redirect('publisher');
if ( ! $publisher = Model_Publisher::find($id))
{
Session::set_flash('error', 'Could not find publisher #'.$id);
Response::redirect('publisher');
}
return Response::forge(
View::forge('publisher/view')
->set('title', 'Publisher')
->set('publisher', $publisher)
);
}
}
これをプレゼンタを使って記述すると, ビューに必要な $id
をコントローラからプレゼンタへ渡したくなりますが, プレゼンタから $id
が取得できるなら, ビューに関する処理としてプレゼンタに閉じ込めることができそうです.
今回の場合は, ルーティングの設定でパラメータの名前を設定して Request::param()
で取得できるようにします.
return array(
'publisher/view/:id' => 'publisher/view/$1',
);
これでプレゼンタからモデルへアクセスして値が取得できるようになりました.
class Presenter_Publisher_View extends Presenter
{
public function view()
{
$this->set('title', 'Publisher');
$this->set('publisher', Model_Publisher::find(Request::active()->param('id')));
}
}
コントローラではPresenterオブジェクトを生成するのみです.
class Controller_Publisher extends Controller
{
public function action_view($id = null)
{
is_null($id) and Response::redirect('publisher');
if ( ! Model_Publisher::find($id))
{
Session::set_flash('error', 'Could not find publisher #'.$id);
Response::redirect('publisher');
}
return Response::forge(
Presenter::forge('publisher/view')
);
}
}
Templateコントローラとの併用
どうしてもコントローラでテンプレートにデータを渡すことになるので, ビューに関する処理がコントローラから切り離せません.
Templateコントローラとプレゼンタは併用しないほうが, プレゼンタの機能が活きるように思います.