Help us understand the problem. What is going on with this article?

FuelPHPのプレゼンタ(Presenter)を使う

More than 3 years have passed since last update.

FuelPHPのプレゼンタ(Presenter)について使い方を調べてみました.

プレゼンタとは

MVC(Model-View-Controller)モデルの派生であるMVP(Model-View-Presenter)モデルのPresenterのような機能です.
MVPモデルそのものについては, こちらの記事が参考になりました.

もう少し具体的に, FuelPHPのマニュアルから, FuelPHPが言うところのプレゼンタの役割を抜粋してみました.

  • ビューに関するロジックを抽象化してコントローラから切り離すために使う
  • データの操作はすべきでない
  • データベース他の呼び出しを含める
  • ビューの生成に必要なデータを準備する操作を含める

FuelPHPのプレゼンタについては, こちらの記事が参考になりました.

Presenterクラスはバージョン1.7.2でViewModelクラスから改名された経緯があります.

プレゼンタを使う

サンプルとして, 蔵書システムの出版社情報を管理する機能を考えます.
出版社情報を登録する入力フォームを表示するアクションメソッドを, Viewクラスを使って記述すると, このようになります.

fuel/app/classes/controller/publisher.php
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() の中身は入力フォーム生成のための処理なので, その処理をごっそりプレゼンタへ移します. 値をビューに渡す処理のみ調整します.

fuel/app/classes/presenter/publisher/create.php
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 を指定することになります.

fuel/app/classes/controller/publisher.php
class Controller_Publisher extends Controller
{
    public function get_create()
    {
        return Response::forge(
            Presenter::forge('publisher/create')
        );
    }
}

コントローラが随分すっきりしました.

コントローラからプレゼンタへ値を渡したい...?

POSTされたidでfindした情報を表示したい, みたいな場合です.
出版社情報を表示するアクションメソッドを, Viewクラスを使って記述すると, このようになります.

fuel/app/classes/controller/publisher.php
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() で取得できるようにします.

fuel/app/config/routes.php
return array(
    'publisher/view/:id' => 'publisher/view/$1',
);

これでプレゼンタからモデルへアクセスして値が取得できるようになりました.

fuel/app/classes/presenter/publisher/view.php
class Presenter_Publisher_View extends Presenter
{
    public function view()
    {
        $this->set('title', 'Publisher');
        $this->set('publisher', Model_Publisher::find(Request::active()->param('id')));
    }
}

コントローラではPresenterオブジェクトを生成するのみです.

fuel/app/classes/controller/publisher.php
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コントローラとプレゼンタは併用しないほうが, プレゼンタの機能が活きるように思います.

sskre
主にPHPを書いています。趣味は低山に登ったり走ったりすることです。
x-trans
AWS、GCP、Azureの導入設計、環境構築、運用・保守までサポートするエンジニア軍団
https://x-trans.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away