アクションヘルパー

導入

アクションヘルパーを使用すると、Zend_Controller_Action を継承した任意のアクションコントローラに対して 実行時やその他必要に応じて機能を追加できます。 アクションヘルパーの狙いは、 アクションコントローラに共通機能を追加するために いちいち抽象クラスを継承する手間を省くことにあります。

アクションヘルパーにはさまざまな使用法があります。 たとえば、Zend_View_HelperZend_Controller_Plugin と同じように、処理の仲買をするために用いることもできます。 アクションヘルパーは (Zend_View_Helper と同様に)、 必要になった時点で読み込むこともできますし、 リクエスト時 (起動時) やアクションコントローラの作成時 (init()) で読み込むこともできます。詳細は、以下の使用例を参照ください。

ヘルパーの初期化

ヘルパーを初期化するにはいくつかの方法があります。 必要に応じて、またそのヘルパーの機能に応じて使い分けましょう。

ヘルパーブローカは、Zend_Controller_Action$_helper に格納されます。 このブローカを使用して、ヘルパーを取得したりコールしたりします。 以下のような方法があります。

  • 明示的に getHelper() を使用します。 ヘルパーの名前を指定すると、 そのヘルパーオブジェクトが返されます。

    $flashMessenger = $this->_helper->getHelper('FlashMessenger');
    $flashMessenger->addMessage('先ほどのリクエストで、あることをしました');
    
  • ヘルパーブローカの __get() 機能を使用すると、 まるでブローカのプロパティであるかのようにヘルパーを操作できます。

    $flashMessenger = $this->_helper->FlashMessenger;
    $flashMessenger->addMessage('先ほどのリクエストで、あることをしました');
    
  • たいていのアクションヘルパーは direct() メソッドを実装しており、 これはそのヘルパーのデフォルトメソッドをコールします。 FlashMessenger の例では、 addMessage() をコールします。

    $this->_helper->FlashMessenger('先ほどのリクエストで、あることをしました');
    

注記

これらの例は、すべて同じことを行っています。

ヘルパーのインスタンスを明示的に作成したいと考えるかもしれません。 たとえばアクションコントローラ以外からヘルパーを使用したいだとか、 すべてのアクションのヘルパーブローカに同じヘルパーを渡したいだとかいった場合です。 インスタンスを作成する方法は、通常の PHP のクラスと同じです。

ヘルパーブローカ

Zend_Controller_Action_HelperBroker がヘルパーオブジェクトやそのパスの登録に関する詳細を処理します。 また、必要に応じてそこからヘルパーを取得できます。

ヘルパーをブローカに登録するには addHelper() を使用します。

Zend_Controller_Action_HelperBroker::addHelper($helper);

もちろん、ヘルパーのインスタンスを作成してそれをブローカに渡すという作業は 時間とリソースを消費します。これらの作業の手間をほんの少し省くためのメソッドとして、 addPrefix()addPath() が用意されています。

  • addPrefix() はクラスのプレフィックスを受け取り、 それをもとにヘルパークラスのパスを決定します。 プレフィックスが、Zend Framework のクラス命名規約に沿っているものとみなして、 パスを決定します。

    // My/Action/Helpers/ にある、名前が My_Action_Helpers で始まるヘルパーを追加します
    Zend_Controller_Action_HelperBroker::addPrefix('My_Action_Helpers');
    
  • addPath() は、最初の引数にディレクトリ、 そして二番目の引数にクラスのプレフィックス (デフォルトは 'Zend_Controller_Action_Helper') を指定します。 これは、指定したディレクトリにある指定したプレフィックスのクラスを追加します。

    // Plugins/Helpers/ にある、名前が Helper で始まるヘルパーを追加します
    Zend_Controller_Action_HelperBroker::addPath('./Plugins/Helpers',
                                                 'Helper');
    

これらは静的メソッドなので、コントローラチェイン内の任意の場所で使用できます。 これにより、必要に応じて動的にヘルパーを追加できることになります。

内部的には、ヘルパーブローカは PluginLoader のインスタンス を用いてパスを保持します。静的メソッド getPluginLoader() で PluginLoader を取得することもできますし、また独自の PluginLoader インスタンスを setPluginLoader() で設定することもできます。

ヘルパークラスがヘルパーブローカ内に存在するかどうかを調べるには hasHelper($name) を使用します。$name には、ヘルパーのショートネーム (プレフィックスを除いたもの) を指定します。

// 'redirector' ヘルパーがブローカに登録されているかどうかを調べます
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    echo 'Redirector helper registered';
}

ヘルパーブローかからヘルパーを取得する静的メソッドには、さらに getExistingHelper()getStaticHelper() のふたつがあります。 getExistingHelper() は、すでに起動されているか、 あるいは明示的にヘルパーブローカに登録されているヘルパーのみを取得します。 存在しない場合は例外をスローします。 getStaticHelper()getExistingHelper() と同じですが、 ヘルパースタックに登録されていないヘルパーについてはそのインスタンスを作成しようとします。 自分で設定をしたいヘルパーを取得するには getStaticHelper() がおすすめです。

どちらのメソッドも、引数はひとつだけです。 この引数 $name には、ヘルパーのショートネーム (プレフィックスを除いたもの) を指定します。

// 'redirector' ヘルパーがブローカに登録されているかどうかを調べ、取得します
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    $redirector =
        Zend_Controller_Action_HelperBroker::getExistingHelper('redirector');
}

// あるいは、登録されているかどうかを気にせずに単純に取得します
$redirector =
    Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');
}

最後に、登録済みのヘルパーをブローカから削除するには removeHelper($name) を使用します。$name には、ヘルパーのショートネーム (プレフィックスを除いたもの) を指定します。

// 'redirector' ヘルパーがブローカに登録されている場合にはそれを削除します
if (Zend_Controller_Action_HelperBroker::hasHelper('redirector')) {
    Zend_Controller_Action_HelperBroker::removeHelper('redirector')
}

組み込みのアクションヘルパー

Zend Framework には、いくつかのアクションヘルパーがデフォルトで組み込まれています。 AJAX のオートコンプリート機能用のレスポンスを作成する AutoComplete、 アクションに応じてレスポンスの形式を変更する ContextSwitchAjaxContext、セッション単位のフラッシュメッセージを扱う FlashMessengerJSON 形式へのエンコードとレスポンスの送信を行う Json、 アプリケーション内から内部あるいは外部へのリダイレクトを実装できるようにする Redirector、そして コントローラ内でのビューオブジェクトの設定とビューのレンダリングを自動化する ViewRenderer です。

ActionStack(日本語)

ActionStack ヘルパーは、リクエストをフロントコントローラの ActionStack プラグインに格納します。これにより、 リクエストの実行時にアクションのキューを作成しやすくなります。 このヘルパーは、アクションを追加する際に 新しいリクエストオブジェクトを指定するか アクション - コントローラ - モジュール の設定を指定するかのいずれかを用います。

ActionStack ヘルパーを起動すると ActionStack プラグインが初期化される

ActionStack を起動すると、暗黙のうちに ActionStack プラグインを登録します。 つまり、この機能を使う際に明示的に ActionStack プラグインを登録する必要はないということです。

例139 アクション、コントローラおよびモジュール名によるタスクの追加

単純にアクションとコントローラそしてモジュール (およびオプションでリクエストパラメータ) を指定して Zend_Controller_Action::_forward() をコールするのが一番シンプルな方法です。

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // 2 つのアクションをスタックに格納して
        // /foo/baz/bar/baz をコールします
        // (FooController::bazAction() にリクエスト変数 bar == baz を指定したもの)
        $this->_helper->actionStack('baz',
                                    'foo',
                                    'default',
                                    array('bar' => 'baz'));

        // /bar/bat のコール
        // (BarController::batAction()) を追加します
        $this->_helper->actionStack('bat', 'bar');
    }
}

例140 リクエストオブジェクトによるタスクの追加

時にはリクエストオブジェクトのオブジェクト指向的な部分が使いたいこともあるでしょう。 そんな場合はこのオブジェクトを ActionStack ヘルパーに渡すこともできます。

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // 2 つのアクションをスタックに格納して
        // /foo/baz/bar/baz をコールします
        // (FooController::bazAction() にリクエスト変数 bar == baz を指定したもの)
        $request = clone $this->getRequest();
        // コントローラやモジュールは指定せず、現在の値を使用します
        $request->setActionName('baz')
                ->setParams(array('bar' => 'baz'));
        $this->_helper->actionStack($request);

        // /bar/bat のコール
        // (BarController::batAction()) を追加します
        $request = clone $this->getRequest();
        // モジュールは指定せず、現在の値を使用します
        $request->setActionName('bat')
                ->setControllerName('bar');
        $this->_helper->actionStack($request);
    }
}

AutoComplete

多くの AJAX 用 javascript ライブラリでは、 オートコンプリート機能を提供しています。 これは、ユーザがタイプした内容にマッチする可能性のある候補の一覧を表示するものです。 AutoComplete ヘルパーは、 このような場合に使用できるレスポンスを返すためのものです。

オートコンプリート機能の実装方法は JS ライブラリによって異なるので、 AutoComplete では多くのライブラリで使用する共通機能を抽象化しています。 そして、個々のライブラリにあわせた実装を用意しています。 返り値の型は、JSON 形式の文字列の配列か JSON 形式の配列の配列 (内部の配列は、選択リストを作成する際に使用するメタデータの連想配列) あるいは HTML となります。

どの実装についての基本的な使用法は同じです。

class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // 何かの処理をします...

        // エンコードしたレスポンスを送信します
        $this->_helper->autoCompleteDojo($data);

        // あるいは明示的に
        $response = $this->_helper->autoCompleteDojo
                                  ->sendAutoCompletion($data);

        // あるいは単純にオートコンプリート用のレスポンスを準備します
        $response = $this->_helper->autoCompleteDojo
                                  ->prepareAutoCompletion($data);
    }
}

デフォルトでは以下のような作業を行います。

  • レイアウト機能と ViewRenderer を無効にする。

  • 適切なレスポンスヘッダを設定する。

  • レスポンスボディにエンコード/フォーマットしたデータを設定する。

  • レスポンスを送信する。

このヘルパーでは次のようなメソッドが使用できます。

  • disableLayouts() は、レイアウト機能と ViewRenderer を無効にします。一般に、これは prepareAutoCompletion() の中でコールされます。

  • encodeJson($data, $keepLayouts = false) はデータを JSON 形式にエンコードし、オプションでレイアウト機能の有効/無効 を切り替えます。一般に、これは prepareAutoCompletion() の中でコールされます。

  • prepareAutoCompletion($data, $keepLayouts = false) は、各種具象実装にあわせてレスポンスデータをフォーマットし、 オプションでレイアウト機能の有効/無効を切り替えます。 返り値は実装によって異なります。

  • sendAutoCompletion($data, $keepLayouts = false) は、各種具象実装にあわせてフォーマットしたレスポンスデータを送信します。 これは、prepareAutoCompletion() をコールしたあとでレスポンスを送信します。

  • direct($data, $sendNow = true, $keepLayouts = false) は、このヘルパーをヘルパーブローカのメソッドとしてコールする場合に使用します。 $sendNow フラグは、 sendAutoCompletion()prepareAutoCompletion() のどちらをコールするかを指定するものです。

現在 AutoComplete がサポートしている AJAX ライブラリは、Dojo と Scriptaculous です。

Dojo でのオートコンプリート

Dojo には、オートコンプリートのためだけのウィジェットはありません。 しかし、ComboBox と FilteringSelect のふたつのウィジェットがオートコンプリート機能を持っています。 どちらのウィジェットも、QueryReadStore を実装したデータを必要とします。詳細は dojo.data のドキュメントを参照ください。

Zend Framework では、単純な数値添字の配列を AutoCompleteDojo ヘルパーに渡します。 そうすると、適切な形式の JSON オブジェクトを返します。

// コントローラのアクション内で
$this->_helper->autoCompleteDojo($data);

例141 Zend MVC を使用した、Dojo でのオートコンプリート

Zend MVC で Dojo によるオートコンプリートを使用するには、 いくつかの準備が必要です。オートコンプリートを使用したい ComboBox 用にフォームオブj稀有とを作成し、 オートコンプリートの結果を提供するためのコントローラアクションを作成し、 オートコンプリートアクションに接続するための 独自の QueryReadStore を作成し、 サーバ側でオートコンプリートを行わせるための javascript を作成することになります。

まずは、必要となる javascript を見ていきましょう。 Dojo は javascript によるオブジェクト指向プログラミングを行うための 完全なフレームワークで、ちょうど PHP における Zend Framework のようなものです。その中には、 ディレクトリ構造を用いて擬似的な名前空間を作成する機能もあります。 ここでは、Dojo の配布ファイルの Dojo ディレクトリと同じ階層に 'custom' ディレクトリを作成します。 そのディレクトリの中に TestNameReadStore.js という javascript ファイルを作成し、次のようなコードを書きます。

dojo.provide("custom.TestNameReadStore");
dojo.declare("custom.TestNameReadStore", dojox.data.QueryReadStore, {
    fetch:function (request) {
        request.serverQuery = { test:request.query.name };
        return this.inherited("fetch", arguments);
    }
});

このクラスは、単に Dojo 自身の QueryReadStore クラスを継承したものです。継承元のクラス自体は抽象クラスです。 そこにリクエスト用のメソッドを定義し、'test' 要素に割り当てています。

次に、オートコンプリートを行うためのフォーム要素を作成します。

class TestController extends Zend_Controller_Action
{
    protected $_form;

    public function getForm()
    {
        if (null === $this->_form) {
            $this->_form = new Zend_Form();
            $this->_form->setMethod('get')
                ->setAction(
                    $this->getRequest()->getBaseUrl() . '/test/process'
                )
                ->addElements(array(
                    'test' => array('type' => 'text', 'options' => array(
                        'filters'        => array('StringTrim'),
                        'dojoType'       => array('dijit.form.ComboBox'),
                        'store'          => 'testStore',
                        'autoComplete'   => 'false',
                        'hasDownArrow'   => 'true',
                        'label' => 'Your input:',
                    )),
                    'go' => array('type' => 'submit',
                                  'options' => array('label' => 'Go!'))
                ));
        }
        return $this->_form;
    }
}

ここでは、単に 'test' と 'go' メソッドのみを持つフォームを作成します。 'test' メソッドは、特別な Dojo 固有の属性 dojoType、store、autoComplete および hasDownArrow を追加します。dojoType では、これから ComboBox を作成することを指定します。そして、それを 'testStore' のデータストア (キー 'store') にリンクします。詳細は後ほど説明します。 'autoComplete' を FALSE に設定することで、 最初にマッチしたものを自動選択するのではなく マッチしたものの一覧を表示するよう Dojo に指示します。 最後に 'hasDownArrow' でセレクトボックス風の下向き矢印を作ります。 これで、マッチしたものを表示したり隠したりできるようになります。

では、フォームを表示するためのメソッドと オートコンプリートの処理用のエンドポイントを作成してみましょう。

class TestController extends Zend_Controller_Action
{
    // ...

    /**
     * 最初のページ
     */
    public function indexAction()
    {
        $this->view->form = $this->getForm();
    }

    public function autocompleteAction()
    {
        if ('ajax' != $this->_getParam('format', false)) {
            return $this->_helper->redirector('index');
        }
        if ($this->getRequest()->isPost()) {
            return $this->_helper->redirector('index');
        }

        $match = trim($this->getRequest()->getQuery('test', ''));

        $matches = array();
        foreach ($this->getData() as $datum) {
            if (0 === strpos($datum, $match)) {
                $matches[] = $datum;
            }
        }
        $this->_helper->autoCompleteDojo($matches);
    }
}

autocompleteAction() ではいくつかの作業を行っています。 まず、POST リクエストを受け取ったことを確実にし、 'format' パラメータの値を 'ajax' に設定します。 これにより、余計なクエリがアクションに送られることを減らします。 次に、'test' パラメータの内容を確認し、私たちのデータと比較します (ここでは、getData() の実装は意図的に控えています。 何らかのデータソースを使用することになるでしょう)。 最後に、マッチした内容を AutoCompletion ヘルパーに送信します。

これでバックエンド側の準備はすべて整いました。 次に、ページのビュースクリプト側ではどうすればいいのかを考えてみましょう。 まず、データストアを用意しなければなりません。 次にフォームをレンダリングし、最後に適切な Dojo ライブラリ (使用するデータストアも含む) を読み込みます。 ビュースクリプトを見てみましょう。 適宜コメントを入れてあります。

<?php // データストアの準備 ?>
<div dojoType="custom.TestNameReadStore" jsId="testStore"
    url="<?php echo $this->baseUrl() ?>/unit-test/autocomplete/format/ajax"
    requestMethod="get"></div>

<?php // フォームのレンダリング ?>
<?php echo $this->form ?>

<?php // Dojo 関連の CSS の、HTML head での読み込み ?>
<?php $this->headStyle()->captureStart() ?>
@import "<?php echo $this->baseUrl()
?>/javascript/dijit/themes/tundra/tundra.css";
@import "<?php echo $this->baseUrl() ?>/javascript/dojo/resources/dojo.css";
<?php $this->headStyle()->captureEnd() ?>

<?php // 必要な Dojo ライブラリを含む javascript の、
   // HTML head での読み込み ?>
<?php $this->headScript()
        ->setAllowArbitraryAttributes(true)
        ->appendFile($this->baseUrl() . '/javascript/dojo/dojo.js',
            'text/javascript',
            array('djConfig' => 'parseOnLoad: true'))
        ->captureStart() ?>
djConfig.usePlainJson=true;
dojo.registerModulePath("custom","../custom");
dojo.require("dojo.parser");
dojo.require("dojox.data.QueryReadStore");
dojo.require("dijit.form.ComboBox");
dojo.require("custom.TestNameReadStore");
<?php $this->headScript()->captureEnd() ?>

headStyle や headScript といったビューヘルパーのコールに注意しましょう。 これらはプレースホルダで、ビュースクリプトをレンダリングする際に HTML の head セクションとなります。

これで、Dojo のオートコンプリートを動作させるための準備がすべて整いました。


Scriptaculous でのオートコンプリート

Scriptaculous は、所定の形式の HTML レスポンスを受け取ることを想定しています。

このライブラリで使用するヘルパーは 'AutoCompleteScriptaculous' です。 このヘルパーにデータの配列を渡せば、Ajax.Autocompleter に対応した形式の HTML レスポンスができあがります。

ContextSwitch および AjaxContext

ContextSwitch アクションヘルパーは、 リクエストに対してさまざまなレスポンスを返す機能を実現するためのものです。 AjaxContext ヘルパーは ContextSwitch をより特化したもので、 レスポンスを XmlHttpRequests で返す機能を提供します。

いずれかを有効にするには、コントローラに対して 「どのアクションがどのコンテキストに対応するのか」 を教えてやる必要があります。 やってきたリクエストがそのアクションで有効なコンテキストである場合、 ヘルパーが行う処理は次のようになります。

  • レイアウト機能が有効な場合に、それを無効にする。

  • 別のビューサフィックスを設定し、 コンテキストに応じて別のビュースクリプトを効率よく扱えるようにする。

  • コンテキストに応じて適切なレスポンスヘッダを送信する。

  • オプションで、指定したコールバックを実行して コンテキストの設定や後処理を行う。

たとえば、次のようなコントローラを考えてみましょう。

class NewsController extends Zend_Controller_Action
{
    /**
     * トップページは listAction() に転送します
     */
    public function indexAction()
    {
        $this->_forward('list');
    }

    /**
     * ニュースの一覧
     */
    public function listAction()
    {
    }

    /**
     * ニュースの閲覧
     */
    public function viewAction()
    {
    }
}

ここで、listAction() の結果を XML 形式でも返せるようにしたくなったとしましょう。 わざわざ別のアクションを作らなくても、 XML でレスポンスを返すように指示できます。

class NewsController extends Zend_Controller_Action
{
    public function init()
    {
        $contextSwitch = $this->_helper->getHelper('contextSwitch');
        $contextSwitch->addActionContext('list', 'xml')
                      ->initContext();
    }

    // ...
}

これが何を行っているかというと、

  • レスポンスヘッダ 'Content-Type' を 'application/xml' にします。

  • ビューのサフィックスを 'xml.phtml' (あるいは別のサフィックスをを使っているなら 'xml.[your suffix]') に変更します。

さて、次は新しいビュースクリプト 'news/list.xml.phtml' を作成しましょう。これが XML の作成とレンダリングを行います。

あるリクエストがコンテキストスイッチを起動するかどうかを判断するために、 このヘルパーはリクエストオブジェクト内のトークンを調べます。 デフォルトでは 'format' というパラメータを調べることになっていますが、 これは変更することもできます。つまり、 ほとんどの場合は、リクエストに 'format' パラメータを追加するだけで コンテキストスイッチを行えるということです。

  • URL のパラメータで指定する場合: /news/list/format/xml (デフォルトのルーティング方式では、アクションに続けて任意の キー/値 のペアを指定できたことを思い出しましょう)

  • GET パラメータで指定する場合: /news/list?format=xml

ContextSwitch では任意のコンテキストを指定できます。 つまり (もし存在するなら) サフィックスを自由に変更したり 送信するレスポンスヘッダを任意のものに変更したり、 任意のコールバックで初期化や後処理を行ったりができるということです。

デフォルトで使用できるコンテキスト

ContextSwitch ヘルパーで 使用できるデフォルトのコンテキストは、json と XML のふたつです。

  • JSONJSON コンテキストは、 'Content-Type' レスポンスヘッダを 'application/json' に設定し、 ビュースクリプトのサフィックスを 'json.phtml' とします。

    しかし、デフォルトではビュースクリプトは不要です。 これは、すべてのビュー変数を単純にシリアライズして JSON レスポンスを直接発行するものです。

    自動 JSON シリアライズ機能を使わないようにすることもできます。

    $this->_helper->contextSwitch()->setAutoJsonSerialization(false);
    
  • XMLXML コンテキストは、 'Content-Type' レスポンスヘッダを 'application/xml' に設定し、 ビュースクリプトのサフィックスを 'xml.phtml' とします。 このコンテキスト用に、新しいビュースクリプトを作成する必要があります。

独自のコンテキストの作成

デフォルトのコンテキストだけでは対応しきれないこともあるでしょう。 たとえば結果を YAML で返したり、PHP のシリアライズ文字列で返したり、 あるいは RSSATOM フィードで返したりといったようにです。 ContextSwitch を使用すればそれも可能です。

新たなコンテキストを追加する最も簡単な方法は addContext() メソッドを使用することです。 このメソッドの引数は 2 つで、コンテキストの名前と 設定の配列を指定します。設定には、以下のうちのひとつあるいは複数を指定します。

  • suffix: ViewRenderer で登録されているデフォルトのビューサフィックスの 前に追加するサフィックス。

  • headers: ヘッダ/値 のペアの配列で、レスポンスとともに送信したいもの。

  • callbacks: キー 'init' や 'post' を含む配列で、それぞれ コンテキストの初期化や後処理の際に使用する PHP コールバックを指定します。

    初期化コールバックは、ContextSwitch が コンテキストを検出した場合に実行されます。 これを使用して、任意のロジックを実行できます。 たとえば JSON コンテキストでは、 このコールバックを使用して 自動 JSON シリアライズが有効な場合に ViewRenderer を無効化しています。

    後処理はアクションの postDispatch() で発生します。これを使用して、任意のロジックを実行できます。 たとえば JSON コンテキストでは、このコールバックを使用して 自動 JSON シリアライズ機能が有効か無効かを調べています。 有効な場合はビュー変数を JSON にシリアライズしてレスポンスに送信し、 無効な場合は ViewRenderer を再度有効にします。

コンテキストを操作するメソッドには次のようなものがあります。

  • addContext($context, array $spec): 新しいコンテキストを追加する。 そのコンテキストが既に存在する場合は例外をスローします。

  • setContext($context, array $spec): 新しいコンテキストを追加、あるいは既存のコンテキストを上書きする。 addContext() と同じように指定します。

  • addContexts(array $contexts): 複数のコンテキストを一度に追加する。配列 $contexts は、コンテキスト/設定 のペアの配列となります。 既に存在するコンテキストを指定した場合は例外をスローします。

  • setContexts(array $contexts): 新しいコンテキストを追加、あるいは既存のコンテキストを上書きする。 addContexts() と同じように指定します。

  • hasContext($context): そのコンテキストが存在する場合に TRUE、存在しない場合に FALSE を返します。

  • getContext($context): 指定した名前のコンテキストを取得する。 addContext() で使用する設定とあわせた配列を返します。

  • getContexts(): すべてのコンテキストを取得する。 コンテキスト/設定 のペアの配列を返します。

  • removeContext($context): 指定した名前のコンテキストを削除する。成功した場合に TRUE、 そのコンテキストが見つからない場合に FALSE を返します。

  • clearContexts(): すべてのコンテキストを削除する。

アクションごとのコンテキストの設定

使用するコンテキストの設定には 2 通りの方法があります。 コントローラ内で手動で配列を作成する方法、 そして ContextSwitch のメソッドでそれを作成する方法です。

アクションとコンテキストの関連を追加するメソッドは addActionContext() です。 このメソッドには 2 つの引数を指定します。 ひとつはコンテキストを追加したいアクション、 もうひとつはコンテキスト名あるいはコンテキスト名の配列です。 たとえば、次のようなコントローラクラスを考えてみましょう。

class FooController extends Zend_Controller_Action
{
    public function listAction()
    {
    }

    public function viewAction()
    {
    }

    public function commentsAction()
    {
    }

    public function updateAction()
    {
    }
}

ここで、'list' アクションに XML コンテキストを、 そして 'comments' アクションに XML コンテキストと JSON コンテキストを追加してみることにします。これは init() メソッドで行います。

class FooController extends Zend_Controller_Action
{
    public function init()
    {
        $this->_helper->contextSwitch()
             ->addActionContext('list', 'xml')
             ->addActionContext('comments', array('xml', 'json'))
             ->initContext();
    }
}

あるいは、単純に配列プロパティ $contexts を設定することもできます。

class FooController extends Zend_Controller_Action
{
    public $contexts = array(
        'list'     => array('xml'),
        'comments' => array('xml', 'json')
    );

    public function init()
    {
        $this->_helper->contextSwitch()->initContext();
    }
}

このほうがオーバーヘッドが少なくなりますが、 書き間違える可能性もあります。

コンテキストの関連付けを行うメソッドには次のようなものがあります。

  • addActionContext($action, $context): ひとつあるいは複数のコンテキストを、あるアクションで使用できるようにする。 関連付けがすでに設定されている場合は、それに追記します。 $context は、単一のコンテキストか コンテキストの配列となります。

    コンテキストとして TRUE を指定すると、 すべてのコンテキストをそのアクションで使用できるようにします。

    $context に空の値を指定すると、 そのアクションではどのコンテキストも使用できないようにします。

  • setActionContext($action, $context): ひとつあるいは複数のコンテキストを、あるアクションで使用できるようにする。 関連付けがすでに設定されている場合は、指定したものでそれを置き換えます。 $context は、単一のコンテキストか コンテキストの配列となります。

  • addActionContexts(array $contexts): いくつかの アクション/コンテキスト のペアを一度に追加する。 $contexts は、アクション/コンテキスト のペアの連想配列です。これは addActionContext() へのプロキシとなります。つまり、既に別のペアが登録されている場合は そこに追記します。

  • setActionContexts(array $contexts): addActionContexts() と同様だが、既存の アクション/コンテキスト のペアは上書きする。

  • hasActionContext($action, $context): 特定のアクションにそのコンテキストが存在するかどうかを調べる。

  • getActionContexts($action = null): 指定したアクションのすべてのコンテキスト、 あるいはすべての アクション/コンテキスト のペアを返す。

  • removeActionContext($action, $context): ひとつあるいは複数のコンテキストを、指定したアクションから削除する。 $context は、単一のコンテキストか コンテキストの配列となります。

  • clearActionContexts($action = null): すべてのコンテキストを、指定したアクションから削除する。 あるいはすべてのアクションのすべてのコンテキストを削除する。

コンテキストスイッチの初期化

コンテキストスイッチを初期化するには、アクションコントローラで initContext() をコールする必要があります。

class NewsController extends Zend_Controller_Action
{
    public function init()
    {
        $this->_helper->contextSwitch()->initContext();
    }
}

時には、使用するコンテキストを決めてしまいたいこともあるでしょう。 たとえば、コンテキストスイッチが起動したときには XML コンテキストだけを使わせたいという場合などです。 その場合は、そのコンテキストを initContext() に渡します。

$contextSwitch->initContext('xml');
追加機能

さまざまなメソッドを使用することで、 ContextSwitch ヘルパーの挙動を変更できます。 たとえば次のようなメソッドが存在します。

  • setAutoJsonSerialization($flag): デフォルトでは、JSON コンテキストはビュー変数をすべてシリアライズし、 JSON 記法にしたものをレスポンスとして返します。 レスポンスを自分で作成したい場合はこれをオフにしなければなりません。 これは、initContext() をコールする前に行う必要があります。

    $contextSwitch->setAutoJsonSerialization(false);
    $contextSwitch->initContext();
    

    このフラグの値を取得するには getAutoJsonSerialization() を使用します。

  • setSuffix($context, $suffix, $prependViewRendererSuffix): このメソッドは、指定したコンテキストに対して 別のサフィックスを設定します。 3 番目の引数を使用すると、 ViewRenderer のサフィックスの前に 新しいサフィックスをつけるのかどうかを指定できます。 このフラグはデフォルトで有効になっています。

    サフィックスに空の値を指定すると、 ViewRenderer のサフィックスのみを使用します。

  • addHeader($context, $header, $content): 指定したコンテキストにレスポンスヘッダを追加します。 $header はヘッダの名前で、 $content はそのヘッダに渡す値となります。

    各コンテキストは複数のヘッダを持つことができます。 addHeader() は、 そのヘッダをコンテキストのヘッダスタックに追加します。

    指定した $header がそのコンテキストに既に存在する場合は、 例外をスローします。

  • setHeader($context, $header, $content): setHeader()addHeader() とほぼ同じですが、 既存のコンテキストヘッダを上書きします。

  • addHeaders($context, array $headers): 指定したコンテキストに一度に複数のヘッダを追加します。 addHeader() へのプロキシとして動作するので、 そのヘッダがすでに存在する場合は例外をスローします。 $headers は ヘッダ/コンテキスト のペアの配列です。

  • setHeaders($context, array $headers.): addHeaders() と似ていますが、これは setHeader() へのプロキシとして動作し、 既存のヘッダは上書きします。

  • getHeader($context, $header): 指定したコンテキストのヘッダの値を取得します。 見つからない場合は NULL を返します。

  • removeHeader($context, $header): 指定したコンテキストの単一のヘッダを削除します。

  • clearHeaders($context, $header): 指定したコンテキストのすべてのヘッダを削除します。

  • setCallback($context, $trigger, $callback): 指定したコンテキストにおける指定したトリガーのコールバックを設定します。 トリガーに指定できる値は 'init' あるいは 'post' (それぞれ、コンテキストの初期化時と postDispatch 時を表します) です。 $callbackPHP のコールバックとして正しい形式でなければなりません。

  • setCallbacks($context, array $callbacks): 指定したコンテキストに複数のコールバックを設定します。 $callbacks は トリガー/コールバック のペアとなります。実際のところ、登録できるコールバックは ほとんどふたつだけで、初期化用のものと後処理用のものです。

  • getCallback($context, $trigger): 指定したコンテキストにおける指定したトリガーのコールバックを取得します。

  • getCallbacks($context): 指定したコンテキストにおけるすべてのコールバックを取得します。 トリガー/コールバック のペアを返します。

  • removeCallback($context, $trigger): 指定したコンテキストにおける指定したトリガーのコールバックを削除します。

  • clearCallbacks($context): 指定したコンテキストにおけるすべてのコールバックを削除します。

  • setContextParam($name): コンテキストスイッチが要求されたかどうかを調べるための リクエストパラメータを設定します。デフォルトは 'format' ですが、このアクセサを使用することで変更できます。

    getContextParam() で、現在の値を取得できます。

  • setAutoDisableLayout($flag): デフォルトでは、コンテキストスイッチが発生したときには レイアウト機能が無効になります。これは、 レイアウト機能は通常は普通のレスポンスの時に使用するものであって それ以外のコンテキストでは無意味だからです。 しかし、時にはレイアウト機能を使いたいこともあるでしょう (新しいコンテキスト用のレイアウトがある場合など)。 その場合は、setAutoDisableLayout()FALSE を渡します。これは、 initContext() をコールするより 前に 行わなければなりません。

    このフラグの現在の値を取得するには、アクセサ getAutoDisableLayout() を使用します。

  • getCurrentContext() を使うと、 現在のコンテキストを取得できます。 コンテキストスイッチが発生していない場合や initContext() の起動前にコールした場合は NULL を返します。

AjaxContext の機能

AjaxContext ヘルパーは ContextSwitch を継承したものです。 ContextSwitch の機能はすべて使用できます。 しかし、いくつか重要な違いがあります。

まず、コンテキストを決めるアクションコントローラのプロパティは $ajaxable となります。これにより、 AJAX 用と通常の HTTP リクエスト用で別のコンテキストを使用できるようになります。 AjaxContext の *ActionContext()* 系のメソッドは、このプロパティに書き込みます。

次に、これは XmlHttpRequest が発生した場合にのみ起動します。 リクエストオブジェクトの isXmlHttpRequest() メソッドで判断します。したがって、たとえコンテキストパラメータ ('format') をリクエストで渡したとしても、そのリクエストが XmlHttpRequest でない場合はコンテキストスイッチが発生しません。

3 番目に、AjaxContextHTML コンテキストを追加します。 このコンテキストでは、サフィックスを 'ajax.phtml' として通常のリクエストのコンテキストと区別しています。 追加のヘッダは返しません。

例142 Ajax リクエストに対してアクションに応答させる

この例では、アクション 'view'、'form' および 'process' に対する AJAX リクエストにレスポンスを返させるようにしています。 最初のふたつ 'view' および 'form' では、HTML コード片を返してページを更新させます。最後の 'process' については JSON を返しています。

class CommentController extends Zend_Controller_Action
{
    public function init()
    {
        $ajaxContext = $this->_helper->getHelper('AjaxContext');
        $ajaxContext->addActionContext('view', 'html')
                    ->addActionContext('form', 'html')
                    ->addActionContext('process', 'json')
                    ->initContext();
    }

    public function viewAction()
    {
        // 単一のコメントを表示します
        // AjaxContext の場合は comment/view.ajax.phtml
        // を使用します
    }

    public function formAction()
    {
        // 新規コメントの追加フォームをレンダリングします
        // AjaxContext の場合は comment/form.ajax.phtml
        // を使用します
    }

    public function processAction()
    {
        // 新規コメントを処理します
        // 結果を JSON で返します。結果をビュー変数に格納するだけで、
        // JSON でそれを返してくれます
    }
}

クライアント側では、AJAX ライブラリからエンドポイント '/comment/view'、'/comment/form' そして '/comment/process' へリクエストを送ることになります。 その際に、'format' パラメータを '/comment/view/format/html'、'/comment/form/format/html' そして '/comment/process/format/json' のように指定します (あるいはクエリ文字列で "?format=json" のようにしてもかまいません)。

ライブラリ側で 'X-Requested-With: XmlHttpRequest' ヘッダが設定されていれば、 このアクションは適切な形式でレスポンスを返します。


FlashMessenger

Introduction

The FlashMessenger helper allows you to pass messages that the user may need to see on the next request. To accomplish this, FlashMessenger uses Zend_Session_Namespace to store messages for future or next request retrieval. It is generally a good idea that if you plan on using Zend_Session or Zend_Session_Namespace, that you initialize with Zend_Session::start() in your bootstrap file. (See the Zend_Session documentation for more details on its usage.)

Available Methods

General methods:

  • setNamespace($namespace='default') is used to set the namespace into which messages are stored by default.

  • getNamespace() is used to retrieve the name of the default namespace. The default namespace is 'default'.

  • resetNamespace() is used to reset the namespace name to the default value, 'default'.

Methods for manipulating messages set in the previous request:

  • hasMessages($namespace=NULL) is used to determine if messages have been carried from a previous request by the flash messenger. The optional argument $namespace specifies which namespace to look in. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

  • getMessages($namespace=NULL) is used to retrieve the messages which have been carried from a previous request by the flash messenger. The optional argument $namespace specifies which namespace to pull from. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

  • getIterator($namespace=NULL) wraps the return value of getMessages() in an instance of ArrayObject. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

  • count($namespace=NULL) returns the number of messages contained in the specified namespace. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

  • clearMessages($namespace=NULL) is used to clear all the messages which have been carried from a previous request by the flash messenger. The optional argument $namespace specifies which namespace to clear out. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

Methods for manipulating messages set in the current request:

  • addMessage($message, $namespace=NULL) is used to add a new message to the current request. $message contains the message to be added, and the optional argument $namespace will specify the namespace. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

  • hasCurrentMessages($namespace=NULL) is used to determine if messages have been added to the flash messenger during the current request. The optional argument $namespace specifies which namespace to look in. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

  • getCurrentMessages($namespace=NULL) is used to retrieve the messages which have been added to the flash messenger during the current request. The optional argument $namespace specifies which namespace to pull from. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

  • clearCurrentMessages($namespace=NULL) is used to clear all the messages which have been added to the flash messenger during the current request. The optional argument $namespace specifies which namespace to clear out. If the $namespace argument is omitted, the value returned by getNamespace() will be used.

Basic Usage Example

The usage example below shows the use of the flash messenger at its most basic. When the action /some/my is called, it adds the flash message "Record Saved!" A subsequent request to the action /some/my-next-request will retrieve it (and thus delete it as well).

class SomeController extends Zend_Controller_Action
{
    /**
     * FlashMessenger
     *
     * @var Zend_Controller_Action_Helper_FlashMessenger
     */
    protected $_flashMessenger = null;

    public function init()
    {
        $this->_flashMessenger =
            $this->_helper->getHelper('FlashMessenger');
        $this->initView();
    }

    public function myAction()
    {
        /**
         * default method of getting
         * Zend_Controller_Action_Helper_FlashMessenger instance
         * on-demand
         */
        $this->_flashMessenger->addMessage('Record Saved!');
    }

    public function myNextRequestAction()
    {
        $this->view->messages = $this->_flashMessenger->getMessages();
        $this->render();
    }
}

JSON

JSON responses are rapidly becoming the response of choice when dealing with AJAX requests that expect dataset responses; JSON can be immediately parsed on the client-side, leading to quick execution.

Usage

Usage is simple: either call it as a method of the helper broker, or call one of the methods encodeJson() or sendJson():

direct($data, $sendNow = true, $keepLayouts = false, $encodeData = true)

sendJson($data, $keepLayouts = false, $encodeData = true)

encodeJson($data, $keepLayouts = false, $encodeData = true)

  • $data: data to encode as JSON

  • $sendNow: flag to define whether to send the JSON data immediately. When true, the helper will immediately set the respose body and exit.

  • $keepLayouts: flag to define whether to enable or disable layours. When false, all layouts are disabled. Optionally, this can be an array of options to pass as the second argument to Zend_Json::encode(). This array of options allows enabling layouts and encoding using Zend_Json_Expr.

  • $encodeData: flag to define whether $data is already JSON-encoded. When true, this helper will not encode $data to JSON before sending.

Keeping Layouts

If you have a separate layout for JSON responses -- perhaps to wrap the JSON response in some sort of context -- each method in the JSON helper accepts an optional argument $keepLayouts: a flag to enable or disable layouts. Passing a boolean TRUE value will keep layouts enabled:

$this->_helper->json($data, true);

Optionally, you can pass an array as the third parameter. This array may contain a variety of options, including the keepLayouts option:

// Direct helper call
$this->_helper->json($data, true, array('keepLayouts' => true);

// ...or, call a method of the helper
$this->_helper->sendJson($data, array('keepLayouts' => true));

Enabling encoding using Zend_Json_Expr

Zend_Json::encode() allows the encoding of native JSON expressions using Zend_Json_Expr objects. This option is disabled by default. To enable this option, pass a boolean TRUE value to the enableJsonExprFinder option:

$this->_helper->json($data, true, array('enableJsonExprFinder' => true);

If you desire to do this, you must pass an array as the third argument. This also allows you to combine other options, such as the keepLayouts option. All such options are then passed to Zend_Json::encode().

$this->_helper->json($data, true, array(
'enableJsonExprFinder' => true,
'keepLayouts'          => true,
));
Example
class FooController extends Zend_Controller_Action
{
    public function barAction()
    {
        // do some processing...
        // Send the JSON response:
        $this->_helper->json($data);

        // or...
        $this->_helper->json->sendJson($data);

        // or retrieve the json:
        $json = $this->_helper->json->encodeJson($data);
    }
}

Redirector(日本語)

導入

Redirector ヘルパーは、 アプリケーション内で必要となるリダイレクト処理用のオブジェクトとして使用します。 _redirect() メソッドと比べた場合の利点としては、 サイト全体で使用する設定を事前に組み込んでおけることがあります。また、 Zend_Controller_Action::_forward() の場合と同様に、組み込みのインターフェイス gotoSimple($action, $controller, $module, $params) が使用できることも利点となります。

Redirector では、 リダイレクトの設定を行うメソッドとして次のようなものが用意されています。

  • setCode() を使用して、 リダイレクトの際に使用する HTTP レスポンスコードを設定します。

  • setExit() を使用して、 リダイレクトの後で強制的に exit() を実行させるようにします。 デフォルトは TRUE です。

  • setGotoSimple() を使用して、gotoSimple() に何も渡されなかったときのデフォルトの URL を設定します。 Zend_Controller_Action::_forward()API である setGotoSimple($action, $controller = null, $module = null, array $params = array()) を使用します。

  • setGotoRoute() を使用して、 登録済みのルートにもとづいた URL を設定します。 キー/値 のペアの配列とルート名を渡し、 それをもとにルートの型と定義から URL を作成します。

  • setGotoUrl() を使用して、gotoUrl() に何も渡されなかったときのデフォルトの URL を設定します。 URL を表す文字列を受け取ります。

  • setPrependBase() を使用して、 setGotoUrl()gotoUrl() あるいは gotoUrlAndExit() で指定した URL の前にリクエストのベース URL を追加します。

  • setUseAbsoluteUri() を使用すると、 Redirector がリダイレクトの際に絶対 URI を使用するようになります。 このオプションを設定すると、 $_SERVER['HTTP_HOST']$_SERVER['SERVER_PORT']、そして $_SERVER['HTTPS'] の内容をもとにして リダイレクト用の完全な URI を作成します。 このオプションのデフォルト値はオフですが、 将来のリリースではデフォルトで有効になるかもしれません。

さらに、実際のリダイレクトを行うためのメソッドとして以下のものが用意されています。

  • gotoSimple() は、setGotoSimple() (_forward() 風の API) を用いて作成した URL にリダイレクトします。

  • gotoRoute() は、setGotoRoute() (ルートの作成) を用いて作成した URL にリダイレクトします。

  • gotoUrl()setGotoUrl() (URL 文字列の指定) を用いて作成した URL にリダイレクトします。

リダイレクト先の URL を知るには getRedirectUrl() を使用します。 これはいつでも使用できます。

基本的な使用例

例143 オプションの設定

この例ではデフォルトのオプションを少し変更します。 HTTP ステータスコードを 303 にし、リダイレクト後に exit() しないようにして、そしてリダイレクトの際のデフォルト URL を指定しています。

class SomeController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');

        // リダイレクタのデフォルトのオプションを設定します
        // このオブジェクトはヘルパーブローカに登録されるので、
        // これ以降のすべてのアクションで有効となります
        $this->_redirector->setCode(303)
                          ->setExit(false)
                          ->setGotoSimple("this-action",
                                          "some-controller");
    }

    public function myAction()
    {
        /* 何かを行います */

        // 先ほど登録した URL にリダイレクトし、その後で
        // exit() します
        $this->_redirector->redirectAndExit();
        return; // 決してここには到達しません
    }
}

例144 デフォルト設定の使用

この例ではデフォルト設定を使用しています。 つまり、リダイレクトするとすぐに exit() が実行されるということです。

// 別の例
class AlternativeController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }

    public function myAction()
    {
        /* 何かを行います */

        $this->_redirector
            ->gotoUrl('/my-controller/my-action/param1/test/param2/test2');
        return; // リダイレクト後に自動的に exit() されるので、決してここには到達しません
    }
}

例145 goto() での _forward() API の使用

gotoSimple()API は、 Zend_Controller_Action::_forward() と同じ形式です。違う点は、このメソッドは渡されたパラメータから URL を作成し、デフォルトルータのデフォルトフォーマットである :module/:controller/:action/* を使用するということです。 また、アクションチェインではなくリダイレクトを行います。

class ForwardController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }

    public function myAction()
    {
        /* 何かを行います */

        // 現在のモジュールの 'my-controller' コントローラの
        // 'my-action' アクションにリダイレクトします。
        // パラメータは param1 => test、param2 => test2 となります。
        $this->_redirector->gotoSimple('my-action',
                                       'my-controller',
                                       null,
                                       array('param1' => 'test',
                                             'param2' => 'test2'
                                             )
                                       );
    }
}

例146 gotoRoute() でのルートアセンブリの使用

次の例は、ルータassemble() メソッドを使用して、 パラメータで指定した連想配列に基づく URL を作成しています。 次のようなルートが登録されているものと仮定します。

$route = new Zend_Controller_Router_Route(
    'blog/:year/:month/:day/:id',
    array('controller' => 'archive',
          'module' => 'blog',
          'action' => 'view')
);
$router->addRoute('blogArchive', $route);

year を 2006、month を 4、day を 24、そして ID を 42 として配列を渡すと、結果の URL/blog/2006/4/24/42 となります。

class BlogAdminController extends Zend_Controller_Action
{
    /**
     * Redirector - defined for code completion
     *
     * @var Zend_Controller_Action_Helper_Redirector
     */
    protected $_redirector = null;

    public function init()
    {
        $this->_redirector = $this->_helper->getHelper('Redirector');
    }

    public function returnAction()
    {
        /* 何かを行います */

        // blog の過去記事にリダイレクトします。URL は
        // /blog/2006/4/24/42 になります。
        $this->_redirector->gotoRoute(
            array('year' => 2006,
                  'month' => 4,
                  'day' => 24,
                  'id' => 42),
            'blogArchive'
        );
    }
}

ViewRenderer(日本語)

導入

ViewRenderer ヘルパーは、 以下のような要件を満たすために作られたものです。

  • コントローラ内でいちいちビューオブジェクトのインスタンスを 作成しなくても済むようにする。 ビューオブジェクトは自動的にコントローラに登録されます。

  • ビュースクリプトやヘルパー、そしてフィルタのパスを 現在のモジュールに基づいて自動的に設定し、 モジュール名をヘルパーやフィルタのクラス名の先頭に自動的に関連付ける。

  • すべてのコントローラとアクションで使用できる グローバルなビューオブジェクトを作成する。

  • すべてのコントローラで使用する、 デフォルトのビューレンダリングオプションを設定できるようにする。

  • 何も指定しなくても、 自動的にビュースクリプトをレンダリングできる機能を追加する。

  • ビューの基底パスやビュースクリプトのパスを 独自に指定できるようにする。

注記

_forward()redirect()、 あるいは手動での render() を行う場合は、 自動レンダリングは不要です。これらの処理を行う場合は、 出力を自前で行うことを ViewRenderer に対して指示します。

注記

ViewRenderer はデフォルトで有効になっています。 これを無効にするには、フロントコントローラのパラメータ noViewRenderer を指定する ($front->setParam('noViewRenderer', true);) か、 あるいはヘルパーブローカからヘルパーを削除 (Zend_Controller_Action_HelperBroker::removeHelper('viewRenderer')) します。

フロントコントローラでのディスパッチ処理の前に ViewRenderer の設定を変更したい場合は、 次のいずれかの方法を使用します。

  • 独自の ViewRenderer のインスタンスを作成し、 ヘルパーブローカにそれを渡して登録する。

    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
    $viewRenderer->setView($view)
                 ->setViewSuffix('php');
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
    
  • ViewRenderer オブジェクトを、 ヘルパーブローカから必要に応じて作成、取得する。

    $viewRenderer =
        Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
    $viewRenderer->setView($view)
                 ->setViewSuffix('php');
    
API

もっとも基本的な使用法は、単に ViewRenderer のインスタンスを作成してそれをヘルパーブローカに渡すというものです。 インスタンスの作成と登録を一度に行うには、ヘルパーブローカの getStaticHelper() メソッドを使用するのがいちばん簡単です。

Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

アクションコントローラのインスタンスが最初に作成されたときに、 ViewRenderer がビューオブジェクトのインスタンスを作成します。 コントローラのインスタンスが作成されるたびに、ViewRendererinit() が呼び出されます。 ここでアクションコントローラのビュープロパティを設定し、 現在のモジュールからの相対パスを指定して addScriptPath() を呼び出します。 これは現在のモジュール名に基づいたプレフィックスをクラス名の先頭につけて呼び出されるので、 ヘルパーやフィルタのクラスをモジュール内で効率的に管理できます。

postDispatch() が呼び出されるたびに、現在のアクションの render() を自動的に呼び出します。

例として、次のようなクラスを考えてみましょう。

// foo モジュールのコントローラクラス
class Foo_BarController extends Zend_Controller_Action
{
    // デフォルトで bar/index.phtml をレンダリングするので、特に何もする必要はありません
    public function indexAction()
    {
    }

    // 変数 'foo' の値を 'bar' に設定して bar/populate.phtml をレンダリングします
    // ビューオブジェクトは既に preDispatch() で定義されているので、既に使用可能です
    public function populateAction()
    {
        $this->view->foo = 'bar';
    }
}

...

// ビュースクリプトの中では、たとえば次のように書きます
$this->foo(); // Foo_View_Helper_Foo::foo() を呼び出します

ViewRenderer には、 ビューのオプションを取得したり設定したりするためのメソッドも豊富に用意されています。

  • setView($view)ViewRenderer が使用するビューオブジェクトを設定します。 これは、クラスのプロパティ $view の値を設定します。

  • setNeverRender($flag = true) を使用すると、自動レンダリング機能を全体的に (すべてのコントローラに対して)無効にしたり有効にしたりできます。 TRUE を指定すると、そのコントローラの postDispatch() では render() を呼び出さなくなります。 getNeverRender() は、現在の設定を取得します。

  • setNoRender($flag = true) を使用すると、自動レンダリングを無効にしたり有効にしたりできます。 TRUE を指定すると、現在のコントローラの postDispatch() では render() を呼び出さなくなります。 この設定は、preDispatch() が呼び出されるたびにいったんリセットされます (つまり、自動レンダリングを無効にしたいすべてのコントローラで 個々にこれを設定する必要があるということです)。 getNoRender() は、現在の設定を取得します。

  • setNoController($flag = true) を使用すると、render() がコントローラ名のサブディレクトリにあるアクションスクリプトを 読みにいかなくできます (デフォルトでは読みにいきます)。 getNoController() は、現在の設定を取得します。

  • setNeverController($flag = true)setNoController() と似ていますが、 こちらは全体に影響を与えます。つまり、 ディスパッチ処理を行っても設定はリセットされません。 getNeverController() は、現在の設定を取得します。

  • setScriptAction($name) を使用すると、レンダリングするアクションスクリプトを指定できます。 $name は、スクリプト名から拡張子を除いたもの (そして、noController が指定されていない限り、 コントローラのディレクトリ名も除いたもの) となります。 指定しなかった場合は、リクエストオブジェクト内のアクションに基づいた名前の ビュースクリプトを探します。 getScriptAction() は、現在の設定を取得します。

  • setResponseSegment($name) を使用すると、レンダリング結果を出力する レスポンスオブジェクトのセグメント名を指定できます。 指定しなかった場合は、デフォルトのセグメントにレンダリングします。 getResponseSegment() は、現在の設定を取得します。

  • initView($path, $prefix, $options) は、ビューの基底パスを指定します。 また、ヘルパースクリプトとフィルタスクリプトの先頭につけるクラスプレフィックスや ViewRenderer のオプションも設定します。 オプションには、 neverRendernoRendernoControllerscriptAction および responseSegment のいずれかのフラグを指定します。

  • setRender($action = null, $name = null, $noController = false) を使用すると、scriptActionresponseSegment そして noController のいずれかまたは複数を 一度に指定できます。direct() はこのメソッドのエイリアスで、コントローラ内から簡単にコールできます。

    // 現在のアクションスクリプトではなく 'foo' をレンダリングします
    $this->_helper->viewRenderer('foo');
    
    // form.phtml の内容をレスポンスセグメント 'html' にレンダリングします。
    // コントローラのビュースクリプト用サブディレクトリは使用しません。
    $this->_helper->viewRenderer('form', 'html', true);
    

    注記

    setRender() および direct() は、実際にはビュースクリプトをレンダリングしません。 実際にレンダリングを行うのは postDispatch()render() で、 それらのメソッドに対するヒントを指示するだけです。

コンストラクタのオプションとして、 ビューオブジェクトを渡したり ViewRenderer のオプションを渡したりできます。 このオプションで指定できるのは、initView() で説明したフラグと同じものです。

$view    = new Zend_View(array('encoding' => 'UTF-8'));
$options = array('noController' => true, 'neverRender' => true);
$viewRenderer =
    new Zend_Controller_Action_Helper_ViewRenderer($view, $options);

さらに追加のメソッドがあり、 ビューオブジェクトで使用するビューの基底パスを変更できます。 また、ビュースクリプトが自動レンダリングを行う際に使用するパスも変更できます。 これらのメソッドでは、以下のプレースホルダのいずれかあるいは複数が使用できます。

  • :moduleDir は、現在のモジュールの基底ディレクトリを指します (規約では、これはモジュールのコントローラディレクトリの親ディレクトリとなります)。

  • :module は、現在のモジュール名を指します。

  • :controller は、現在のコントローラ名を指します。

  • :action は、現在のアクション名を指します。

  • :suffix は、ビュースクリプトのサフィックス (setViewSuffix() で設定したもの) を指します。

パス指定を制御するメソッドは次のとおりです。

  • setViewBasePathSpec($spec) は、ビューオブジェクトを追加する際に使用する基底パスを 決める際に使用するパス指定を変更します。 デフォルトの設定は :moduleDir/views です。 現在の設定を取得するには getViewBasePathSpec() を使用します。

  • setViewScriptPathSpec($spec) は、個々のビュースクリプトのパス (からビュースクリプトの基底パスを除いた部分) を決める際に使用するパス指定を変更します。 デフォルトの設定は :controller/:action.:suffix です。 現在の設定を取得するには getViewScriptPathSpec() を使用します。

  • setViewScriptPathNoControllerSpec($spec) は、noController が有効な場合に 個々のビュースクリプトのパス (からビュースクリプトの基底パスを除いた部分) を決める際に使用するパス指定を変更します。 デフォルトの設定は :action.:suffix です。 現在の設定を取得するには getViewScriptPathNoControllerSpec() を使用します。

パス指定をよりきめ細かく行うには、 Zend_Filter_Inflector を使用します。実は、ViewRenderer はパスのマッピングを行う際に既にインフレクタを使用しています。 インフレクタに手を入れたい (独自のインフレクタを使用したり、 デフォルトのインフレクタに手を加えたりしたい) 場合は、 以下のメソッドを使用します。

  • getInflector() は、インフレクタを取得します。 まだ ViewRenderer にインフレクタが存在しない場合は、 デフォルトの規則にもとづいたインフレクタを作成します。

    デフォルトでは、サフィックスやモジュールディレクトリへの参照に静的ルールを使用します。 また静的な対象を使用します。これにより、さまざまな ViewRenderer のプロパティから 動的にインフレクタを変更できるようになります。

  • setInflector($inflector, $reference) は、 ViewRenderer で使用する独自のインフレクタを設定します。 $referenceTRUE の場合は、 対象だけでなくサフィックスやモジュールディレクトリも ViewRenderer のプロパティへの静的な参照とします。

デフォルトの検索方式

ViewRenderer は、 パスの正規化を行ってビュースクリプトによる検索を簡単にします。 デフォルトのルールは次のようなものです。

  • :module: MixedCase および camelCase 形式の単語がダッシュで分割され、 すべて小文字になります。たとえば "FooBarBaz" は "foo-bar-baz" となります。

    内部的には、インフレクタはフィルタ Zend_Filter_Word_CamelCaseToDash および Zend_Filter_StringToLower を使用します。

  • :controller: MixedCase および camelCase 形式の単語がダッシュで分割され、 アンダースコアはディレクトリ区切り文字に変換され、 すべて小文字になります。たとえば "FooBar" は "foo-bar" となり、そして "FooBar_Admin" は "foo-bar/admin" となります。

    内部的には、インフレクタはフィルタ Zend_Filter_Word_CamelCaseToDashZend_Filter_Word_UnderscoreToSeparator および Zend_Filter_StringToLower を使用します。

  • :action: MixedCase および camelCase 形式の単語がダッシュで分割され、 英数字以外の文字はダッシュに変換され、 すべて小文字になります。たとえば "fooBar" は "foo-bar" となり、"foo-barBaz" は "foo-bar-baz" となります。

    内部的には、インフレクタはフィルタ Zend_Filter_Word_CamelCaseToDashZend_Filter_PregReplace および Zend_Filter_StringToLower を使用します。

ViewRenderer API の最後に紹介するのは、 実際にビュースクリプトのパスを決定するメソッドと ビューのレンダリングを行うメソッドです。以下をご覧ください。

  • renderScript($script, $name) は、指定したパスのスクリプトをレンダリングします。 オプションで、パスセグメントの名前を指定することもできます。 このメソッドを使用する際には、ViewRenderer はスクリプト名を自動的に決定することはありません。 そのかわりに、$script で指定された内容を直接 ビューオブジェクトの render() メソッドに渡します。

    注記

    レスポンスオブジェクトにビューがレンダリングされると、 自動的に noRender を設定します。 これにより、同じビュースクリプトを間違って複数回レンダリングしてしまうことを防ぎます。

    注記

    デフォルトでは、 Zend_Controller_Action::renderScript()ViewRendererrenderScript() メソッドへのプロキシとなります。

  • getViewScript($action, $vars) は、渡されたアクションや $vars で指定した変数の値に基づいてビュースクリプトのパスを作成します。 $vars 配列のキーは、パスを指定するためのキー ('moduleDir'、'module'、'controller'、'action' および 'suffix') のいずれかとなります。渡された変数の値をもとにしてパスを作成します。 なにも渡されなかった場合は、現在のリクエストの内容をもとにしてパスを作成します。

    getViewScript() は、noController フラグの内容によって viewScriptPathSpec あるいは viewScriptPathNoControllerSpec のいずれかを使用します。

    モジュール名やコントローラ名、アクション名にあらわれる 単語の区切りは、ダッシュ ('-') に置き換えられます。 したがって、たとえばコントローラ名が 'foo.bar' でアクション名が 'baz:bat' だったとすると、 デフォルトのパス指定をもとにしたビュースクリプトのパスは 'foo-bar/baz-bat.phtml' となります。

    注記

    デフォルトでは、 Zend_Controller_Action::getViewScript()ViewRenderergetViewScript() メソッドへのプロキシとなります。

  • render($action, $name, $noController) は、まず $name あるいは $noController が指定されているかどうかを調べます。 指定されている場合は、ViewRenderer の対応するフラグ (それぞれ responseSegment と noController) を設定します。 次に、$action 引数が指定されていれば、 それを getViewScript() に渡します。 最後に、取得したビュースクリプトのパスを renderScript() に渡します。

    注記

    render() を使用する際には、その副作用に注意しましょう。 レスポンスセグメント名や noController フラグに指定した内容は、そのオブジェクト内で残り続けます。 さらに、レンダリングが完了した際に noRender も設定されます。

    注記

    デフォルトでは、 Zend_Controller_Action::render()ViewRendererrender() メソッドへのプロキシとなります。

  • renderBySpec($action, $vars, $name) は、パス指定用の変数を渡してビュースクリプトのパスを決定します。 $action および $vars の内容を getScriptPath() に、そしてその結果得られたスクリプトのパスと $namerenderScript() に渡します。

基本的な使用例

例147 基本的な使用法

最も基本的な使用法は、起動ファイル内で ViewRenderer を作成してヘルパーブローカに登録し、 アクションメソッドで変数の値を設定するというものです。

// 起動ファイル内で
Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

...

// 'foo' モジュールの 'bar' コントローラ
class Foo_BarController extends Zend_Controller_Action
{
    // デフォルトで bar/index.phtml をレンダリングするので、特に何もする必要はありません
    public function indexAction()
    {
    }

    // 変数 'foo' の値を 'bar' に設定して bar/populate.phtml をレンダリングします
    // ビューオブジェクトは既に preDispatch() で定義されているので、既に使用可能です
    public function populateAction()
    {
        $this->view->foo = 'bar';
    }

    // 何もレンダリングせずに別のアクションに転送します
    // 転送先のアクションで何らかのレンダリングを行います
    public function bazAction()
    {
        $this->_forward('index');
    }

    // 何もレンダリングせず別の場所にリダイレクトします
    public function batAction()
    {
        $this->_redirect('/index');
    }
}

命名規約: コントローラ名やアクション名の単語の区切り

コントローラやアクションの名前が複数の単語からなるものである場合、 ディスパッチャには、特定のパスや区切り文字を使用して単語を区切った URL を指定しなければなりません。 ViewRenderer は、コントローラ名の中にあるパス区切り文字を 実際のパス区切り文字 ('/') に置き換え、単語区切り文字をダッシュ ('-') に置き換えてパスを作成します。したがって、 アクション /foo.bar/baz.bat をコールすると FooBarController.phpFooBarController::bazBatAction() へディスパッチされ、 foo-bar/baz-bat.phtml をレンダリングすることになります。 また、アクション /bar_baz/baz-bat をコールすると Bar/BazController.php (パス区切り文字に注意) の Bar_BazController::bazBatAction() へディスパッチされ、bar/baz/baz-bat.phtml をレンダリングすることになります。

二番目の例では、モジュールはデフォルトモジュールのままであることに注意しましょう。 しかし、パス区切り文字があるために、 Bar/BazController.php にある Bar_BazController を受け取ることになります。 ビューレンダラはコントローラのディレクトリ階層を模倣します。

例148 自動レンダリングの無効化

アクションやコントローラによっては、自動レンダリングを無効にしたいこともあるでしょう。 たとえば、HTML 以外 (XMLJSON など) を出力したい場合や 単に何も出力したくない場合などです。 そんな場合には以下のいずれかの方法を使用します。 つまり、すべての自動レンダリングを無効にする (setNeverRender()) か、あるいは現在のアクションでだけ 自動レンダリングを無効にする (setNoRender()) かです。

// Bar モジュールの Baz コントローラクラス
class Bar_BazController extends Zend_Controller_Action
{
    public function fooAction()
    {
        // このアクションでは自動レンダリングを行いません
        $this->_helper->viewRenderer->setNoRender();
    }
}

// Bar モジュールの Bat コントローラクラス
class Bar_BatController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        // このコントローラのアクションでは決して自動レンダリングを行いません
        $this->_helper->viewRenderer->setNoRender();
    }
}

注記

たいていの場合は、自動レンダリングを全体で無効にする (setNeverRender()) のは無意味です。 なぜなら、ViewRenderer の唯一の存在意義が、 ビューオブジェクトを自動的に設定することだからです。

例149 別のビュースクリプトの選択

アクション名から自動的に決まるスクリプトではなく、 それ以外のものをレンダリングしたくなる場合もあるでしょう。 たとえば、add アクションと edit アクションのふたつを持つコントローラがあったとしましょう。 どちらのアクションも同じ 'form' ビューを表示しますが、 そこに設定する値が異なります。 そんな場合に、それぞれでスクリプト名を変えるのは簡単です。 setScriptAction()setRender() を使用するか、あるいはヘルパーをメソッドとして呼び出します。 これは setRender() を起動します。

// Foo モジュールの Bar コントローラクラス
class Foo_BarController extends Zend_Controller_Action
{
    public function addAction()
    {
        // 'bar/add.phtml' ではなく 'bar/form.phtml' をレンダリングします
        $this->_helper->viewRenderer('form');
    }

    public function editAction()
    {
        // 'bar/edit.phtml' ではなく 'bar/form.phtml' をレンダリングします
        $this->_helper->viewRenderer->setScriptAction('form');
    }

    public function processAction()
    {
        // 何かのチェックをした後で...
        if (!$valid) {
            // 'bar/process.phtml' ではなく 'bar/form.phtml' をレンダリングします
            $this->_helper->viewRenderer->setRender('form');
            return;
        }

        // その他の処理を続けます...
    }

}

例150 登録されているビューの変更

ビューオブジェクトの設定を変更したくなったとしましょう。 たとえば、ヘルパーのパスやエンコーディングを変更したくなったらどうしますか? そんな場合は、コントローラに設定されているビューオブジェクトを変更するか、 あるいは ViewRenderer の外部からビューオブジェクトを取得します。 どちらも同じオブジェクトへの参照を取得することになります。

// Foo モジュールの Bar コントローラクラス
class Foo_BarController extends Zend_Controller_Action
{
    public function preDispatch()
    {
        // ビューのエンコーディングを変更します
        $this->view->setEncoding('UTF-8');
    }

    public function bazAction()
    {
        // ビューオブジェクトを取得し、エスケープ用のコールバックを 'htmlspecialchars' に設定します
        $view = $this->_helper->viewRenderer->view;
        $view->setEscape('htmlspecialchars');
    }
}

高度な使用例

例151 パスの指定方法の変更

場合によっては、デフォルトのパス指定があなたのサイトに うまく当てはまらないこともあるでしょう。 たとえば、すべてのテンプレートを単一のディレクトリ配下にまとめ、 デザイナにはそのディレクトリに対するアクセス権だけを与えたいといった場合です (Smarty を使用する場合などにありがちです)。 そんな場合は、ビューの基底パスをハードコーディングし、 それをアクションのビュースクリプトのパスとして使用することになります。

この例では、ビューの基底パスを '/opt/vendor/templates' とし、ビュースクリプトのパスは ':moduleDir/:controller/:action.:suffix' となるようにします。noController フラグが設定されている場合は、サブディレクトリ (':action.:suffix') からではなくトップディレクトリからのパスとして探すことになります。 最後に、ビュースクリプトのファイルの拡張子として 'tpl' を設定します。

/**
 * 起動ファイル
 */

// 別のビュー実装を使用します
$view = new ZF_Smarty();

$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
$viewRenderer->setViewBasePathSpec('/opt/vendor/templates')
             ->setViewScriptPathSpec(':module/:controller/:action.:suffix')
             ->setViewScriptPathNoControllerSpec(':action.:suffix')
             ->setViewSuffix('tpl');
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

例152 単一のアクションから複数のビュースクリプトをレンダリングする例

時には、複数のビュースクリプトをひとつのアクションで処理したいこともあるでしょう。 これは、非常に直感的な方法で実現できます。単に render() を必要なだけコールすればいいのです。

class SearchController extends Zend_Controller_Action
{
    public function resultsAction()
    {
        // $this->model に現在のモデルが設定されているものとします
        $this->view->results =
            $this->model->find($this->_getParam('query', '');

        // render() は、デフォルトでは ViewRenderer へのプロキシとなります。
        // まず form を、そして results をレンダリングします
        $this->render('form');
        $this->render('results');
    }

    public function formAction()
    {
        // 何もしなくても、ViewRenderer が自動的にビュースクリプトをレンダリングします
    }
}

独自のヘルパーの作成

アクションヘルパーは、抽象クラス Zend_Controller_Action_Helper_Abstract を継承して作成します。 ここには、基本的なインターフェイスやヘルパーブローカが使用する必須機能などが含まれています。 具体的には、次のようなメソッドです。

  • setActionController() を使用して、現在のアクションコントローラを設定します。

  • init() はヘルパーブローカによって起動時に実行され、 ヘルパーを初期化します。これは、 アクションチェイン内の複数のコントローラで同一のヘルパーを使用している場合に 状態をリセットする際などに便利です。

  • preDispatch() はディスパッチアクションの前に実行されます。

  • postDispatch() はディスパッチアクションが終了した後で実行されます。 preDispatch() プラグインがアクションの処理をスキップした場合も、 これは実行されます。後始末などをここで行います。

  • getRequest() は現在のリクエストオブジェクトを取得します。

  • getResponse() は現在のレスポンスオブジェクトを取得します。

  • getName() はヘルパーの名前を取得します。 クラス名にアンダースコアが含まれる場合は最後のアンダースコア以降の文字、 そうでない場合はクラス名全体を返します。たとえば、クラス名が Zend_Controller_Action_Helper_Redirector の場合は Redirector を、クラス名が FooMessage の場合はそのままの名前を返します。

オプションで、ヘルパークラスに direct() メソッドを実装することもできます。これを定義しておくと、 ヘルパーブローカのメソッドであるかのようにそのヘルパーを扱えるようになります。 これにより、一度だけ使用するようなヘルパーが扱いやすくなります。 たとえば、redirectordirect()goto() のエイリアスとなっているので、このようにして使用できます。

// /blog/view/item/id/42 にリダイレクトします
$this->_helper->redirector('item', 'view', 'blog', array('id' => 42));

内部的には、まずヘルパーブローカの __call() メソッドが redirector という名前のヘルパーを探し、 それからそのヘルパーで direct() メソッドが定義されているかどうかを調べ、 渡された引数でそのメソッドをコールしています。

独自のヘルパークラスを作成した場合は、 上で説明したようにしてそれを利用できるようにしておきましょう。