# 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 がビューオブジェクトのインスタンスを作成します。 コントローラのインスタンスが作成されるたびに、ViewRenderer の `init()` がコールされます。 ここでアクションコントローラのビュープロパティを設定し、 現在のモジュールからの相対パスを指定して `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 のオプションも設定します。 オプションには、 neverRender、noRender、 noController、scriptAction および responseSegment のいずれかのフラグを指定します。

- `setRender($action = null, $name = null, $noController = false)` を使用すると、scriptAction や responseSegment そして 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 で使用する独自のインフレクタを設定します。 `$reference` が `TRUE` の場合は、 対象だけでなくサフィックスやモジュールディレクトリも 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_CamelCaseToDash`、 `Zend_Filter_Word_UnderscoreToSeparator` および `Zend_Filter_StringToLower` を使用します。

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

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

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

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

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

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

- `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()` は ViewRenderer の `getViewScript()` メソッドへのプロキシとなります。

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

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

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

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

## 基本的な使用例

### 基本的な使用法

最も基本的な使用法は、起動ファイル内で 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.php` の `FooBarController::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` を受け取ることになります。 ビューレンダラはコントローラのディレクトリ階層を模倣します。

### 自動レンダリングの無効化

アクションやコントローラによっては、自動レンダリングを無効にしたいこともあるでしょう。 たとえば、HTML 以外 (XML や JSON など) を出力したい場合や 単に何も出力したくない場合などです。 そんな場合には以下のいずれかの方法を使用します。 つまり、すべての自動レンダリングを無効にする (`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 の唯一の存在意義が、 ビューオブジェクトを自動的に設定することだからです。

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

アクション名から自動的に決まるスクリプトではなく、 それ以外のものをレンダリングしたくなる場合もあるでしょう。 たとえば、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;
            }

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

    }

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

ビューオブジェクトの設定を変更したくなったとしましょう。 たとえば、ヘルパーのパスやエンコーディングを変更したくなったらどうしますか? そんな場合は、コントローラに設定されているビューオブジェクトを変更するか、 あるいは 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');
        }
    }

## 高度な使用例

### パスの指定方法の変更

場合によっては、デフォルトのパス指定があなたのサイトに うまく当てはまらないこともあるでしょう。 たとえば、すべてのテンプレートを単一のディレクトリ配下にまとめ、 デザイナにはそのディレクトリに対するアクセス権だけを与えたいといった場合です (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);

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

時には、複数のビュースクリプトをひとつのアクションで処理したいこともあるでしょう。 これは、非常に直感的な方法で実現できます。単に `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 が自動的にビュースクリプトをレンダリングします
        }
    }