# フロントコントローラ

## 概要

`Zend_Controller_Front` は Model-View-Controller (MVC) アプリケーションで用いられる フロントコントローラパターン を実装したものです。 その役割は、リクエスト環境を初期化してリクエストの配送先を決定し、 見つかった配送先に処理を引き渡すことです。また、 レスポンスの内容を取得してそれをコール元に返します。

`Zend_Controller_Front` は シングルトンパターン も実装しています。つまり、どんな場合でもひとつのインスタンスしか存在しないことになります。 これを利用すると、コントローラをレジストリとして扱えるようになります。

`Zend_Controller_Front` は プラグインブローカ を持っています。これにより、さまざまなイベントをプラグインで処理できるようになります。 開発者は、ディスパッチ処理をカスタマイズして機能を追加する際に フロントコントローラ自体を継承したクラスを作成する必要がなくなります。

アクションコントローラ へのパスを含むディレクトリを最低ひとつは指定しないと、 フロントコントローラは動作しません。 フロントコントローラの動作環境やそのヘルパークラスを変更するために、 さまざまな手法が用意されています。

### デフォルトの挙動

デフォルトでは、フロントコントローラは ErrorHandler プラグインと ViewRenderer アクションヘルパープラグインを読み込みます。 これらにより、コントローラ内でのエラー処理やビューのレンダリングがシンプルに行えるようになります。

ErrorHandler を無効にするには、 `dispatch()` をコールする前のどこかで以下のようにします。

    // ErrorHandler プラグインを無効にします
    $front->setParam('noErrorHandler', true);

ViewRenderer を無効にするには、 `dispatch()` をコールする前に以下を実行します。

    // ViewRenderer ヘルパーを無効にします
    $front->setParam('noViewRenderer', true);

## 主要なメソッド

フロントコントローラには、その環境設定用のメソッドがいくつか用意されています。 そのうち、フロントコントローラの機能の鍵となる主要なメソッドは、以下の3つです。

### getInstance()

`getInstance()` は、フロントコントローラのインスタンスを取得します。 フロントコントローラはシングルトンパターンを実装しているので、 フロントコントローラのインスタンスを作成する唯一の方法はこのメソッドをコールすることとなります。

    $front = Zend_Controller_Front::getInstance();

### setControllerDirectory() および addControllerDirectory

`setControllerDirectory()` は、ディスパッチャ が アクションコントローラ クラスファイルをどこから探せばよいのかを指定するメソッドです。 単一のパスを指定することもできますし、複数のパスを連想配列で指定することもできます。

いくつか例を示します。

    // デフォルトのコントローラディレクトリを設定します
    $front->setControllerDirectory('../application/controllers');

    // 複数のモジュールのディレクトリを一度に指定します
    $front->setControllerDirectory(array(
        'default' => '../application/controllers',
        'blog'    => '../modules/blog/controllers',
        'news'    => '../modules/news/controllers',
    ));

    // 'foo' モジュールのディレクトリを追加します
    $front->addControllerDirectory('../modules/foo/controllers', 'foo');

`addControllerDirectory()` でモジュール名を省略すると、default モジュールが指定されたものとみなします。 もしすでに存在する場合は、それを上書きします。

コントローラディレクトリの現在の設定を取得するには `getControllerDirectory()` を使用します。 これは、モジュールとディレクトリの組を配列で返します。

### addModuleDirectory() および getModuleDirectory()

フロントコントローラのひとつの側面として、モジュラーディレクトリ構造を定義 して単体のコンポーネントを作成するということがあります。 これは "モジュール" と呼ばれます。

書くモジュールは個別のディレクトリになければならず、 またデフォルトモジュールと同じディレクトリ構成でなければなりません。 すなわち、すくなくともサブディレクトリ `/controllers/` がなければならず、 またたいていは `/views/` などの他のサブディレクトリもあるということです。

`addModuleDirectory()` には、ひとつあるいは複数のモジュールディレクトリの名前を渡します。 渡された内容を調べ、それをフロントコントローラのコントローラディレクトリに追加します。

その後、特定のモジュールや現在のモジュールへのパスを知りたい場合に `getModuleDirectory()` をコールします。 モジュール名を渡すと、指定したモジュールのディレクトリを取得することができます。

### dispatch()

`dispatch(Zend_Controller_Request_Abstract $request = null, Zend_Controller_Response_Abstract $response = null)` は、フロントコントローラでもっとも重要な仕事を担当します。 オプションで リクエストオブジェクト や レスポンスオブジェクト を受け取り、それぞれ独自のオブジェクトを指定することができます。

リクエストオブジェクトやレスポンスオブジェクトを省略すると、 `dispatch()` は事前にオブジェクトが登録されているかどうかを確認します。 もし登録されていればそれを使用し、登録されていなければデフォルトのオブジェクトを作成して使用します (どちらの場合についても、HTTP リクエスト/レスポンス オブジェクトをデフォルトで使用します)。

同様に、`dispatch()` は ルータ や ディスパッチャ オブジェクトについても登録済みのものがあるかどうかを確認します。 もしあればそれを使用し、なければデフォルトのオブジェクトを作成して使用します。

ディスパッチ処理は、次の三段階に分けられます。

- ルーティング

- ディスパッチ

- レスポンス

ルーティングは一度だけ発生します。これは、`dispatch()` がコールされた際のリクエストオブジェクトの内容を使用して行います。 ディスパッチは繰り返し行われます。 ひとつのリクエストが複数のアクションを指定している場合や、 コントローラまたはプラグインがリクエストオブジェクトを設定しなおして 別のアクションへディスパッチさせた場合などです。 すべてが終了したら、フロントコントローラはレスポンスを返します。

### run()

`Zend_Controller_Front::run($path)` は静的メソッドで、コントローラを含むディレクトリへのパスを指定します。 このメソッドは getInstance() を使用してフロントコントローラのインスタンスを取得し、 setControllerDirectory() を使用してパスを登録し、最後に ディスパッチ します。

`run()` は、サイト単位の設定などで フロントコントローラのカスタマイズが不要な場合に便利なメソッドです。

    // フロントコントローラを作成してコントローラディレクトリを設定し、
    // ディスパッチするまでをいちどでお手軽に行います
    Zend_Controller_Front::run('../application/controllers');

## 環境へのアクセス用メソッド群

これまでに説明したメソッド以外にもさまざまなアクセス用メソッドが用意されており、 これらを使用してフロンとコントローラの環境にアクセスすることができます。 つまり、フロントコントローラが処理を委譲しているクラスの環境にもアクセスできるということです。

- `resetInstance()` は、現在の設定をすべて消去します。 主にテスト目的で使用しますが、 複数のフロントコントローラを連結させたい場合などに使用することもあります。

- `setDefaultControllerName()` および `getDefaultControllerName()` で、デフォルトのコントローラとして使用する名前を指定したり (指定しなければ 'index' となります) 現在の設定を取得したりできます。 これらメソッドは、 ディスパッチャ へのプロキシです。

- `setDefaultAction()` および `getDefaultAction()` で、デフォルトのアクションとして使用する名前を指定したり (指定しなければ 'index' となります) 現在の設定を取得したりできます。 これらのメソッドは ディスパッチャ へのプロキシです。

- `setRequest()` および `getRequest()` は、ディスパッチ処理で使用する リクエスト クラスやオブジェクトを指定したり、現在のオブジェクトを取得したりします。 リクエストオブジェクトを指定するときに、クラス名を指定することができます。 この場合、このメソッドは指定したクラスファイルを読み込んでインスタンスを作成します。

- `setRouter()` および `getRouter()` は、ディスパッチ処理で使用する ルータ クラスやオブジェクトを指定したり、現在のオブジェクトを取得したりします。 ルータオブジェクトを指定するときに、クラス名を指定することができます。 この場合、このメソッドは指定したクラスファイルを読み込んでインスタンスを作成します。

  ルータオブジェクトを取得する際には、まずルータが存在するかどうかを調べ、 存在しない場合にはデフォルトのルータ (rewrite ルータ) のインスタンスを作成します。

- `setBaseUrl()` および `getBaseUrl()` は、リクエストのルーティング時に URL から取り除く 基底 URL を指定したり、現在の値を取得したりします。 この値は、ルーティングの直前にリクエストオブジェクトに渡されます。

- `setDispatcher()` および `getDispatcher()` は、ディスパッチ処理で使用する ディスパッチャ クラスやオブジェクトを指定したり、現在のオブジェクトを取得したりします。 ディスパッチャオブジェクトを指定するときに、クラス名を指定することができます。 この場合、このメソッドは指定したクラスファイルを読み込んでインスタンスを作成します。

  ディスパッチャオブジェクトを取得する際には、まずディスパッチャが存在するかどうかを調べ、 存在しない場合にはデフォルトのディスパッチャのインスタンスを作成します。

- `setResponse()` および `getResponse()` は、ディスパッチ処理で使用する レスポンス クラスやオブジェクトを指定したり、現在のオブジェクトを取得したりします。 レスポンスオブジェクトを指定するときに、クラス名を指定することができます。 この場合、このメソッドは指定したクラスファイルを読み込んでインスタンスを作成します。

- `registerPlugin(Zend_Controller_Plugin_Abstract $plugin, $stackIndex = null)` は、プラグインオブジェクト を登録します。オプションの `$stackIndex` を設定すると、プラグインの実行順を制御することができます。

- `unregisterPlugin($plugin)` は、 プラグインオブジェクト の登録を解除します。`$plugin` にはプラグインオブジェクトそのものか、あるいはプラグインのクラス名を表す文字列を指定します。 ここで指定したプラグインの登録を解除します。

- `throwExceptions($flag)` で、ディスパッチの際に発生した例外をスローするかどうかを切り替えます。 デフォルトでは、例外はスローされず、 レスポンスオブジェクト に保存されます。`throwExceptions()` をオンにすると、この挙動を変更できます。

  詳細は MVC 例外 を参照ください。

- `returnResponse($flag)` は、フロントコントローラが `dispatch()` からのレスポンスを返す (`TRUE`) かレスポンスを自動的に発行する (`FALSE`) かを切り替えます。デフォルトでは、レスポンスは (`Zend_Controller_Response_Abstract::sendResponse()` によって) 自動的に発行されます。`returnResponse()` をオンにすると、この挙動を変更できます。 behaviour.

  レスポンスを返すようにする理由としては、 実際に発行する前に例外のチェックを行いたり レスポンスの情報 (ヘッダなど) をログに記録したりなどが考えられます。

## フロントコントローラのパラメータ

最初のほうで、フロントコントローラはレジストリとしても使用できると説明しました。 その際に使用するのが "param" 系のメソッド群です。 これらのメソッドを使用すると、任意のデータ (オブジェクトや変数) をフロントコントローラに登録することができます。 登録したデータは、ディスパッチチェイン内のどこででも使用できます。 これらの値は、ルータやディスパッチャそしてアクションコントローラにも渡されます。 各メソッドについて、以下にまとめます。

- `setParam($name, $value)` は、 パラメータ `$name` の値を `$value` に設定します。

- `setParams(array $params)` は、 連想配列を使用して複数のパラメータを一度に設定します。

- `getParam($name)` は、 `$name` で指定した名前のパラメータの値を取得します。

- `getParams()` は、 すべてのパラメータの一覧を一度に取得します。

- `clearParams()` は、 単一のパラメータ (文字列で指定した場合) か 複数のパラメータ (文字列の配列で指定した場合)、 またはすべてのパラメータ (何も指定しなかった場合) を消去します。

ディスパッチチェイン内で特定の目的で使用するために、 いくつかのパラメータが事前に定義されています。

- useDefaultControllerAlways は、 ディスパッチできない (モジュール、コントローラ、アクションのいずれかが存在しない) リクエストに対して、 デフォルトモジュールのデフォルトコントローラにディスパッチするよう ディスパッチャ に指示します。デフォルトではこの機能は無効になっています。

  この設定の使用法についての詳細は 遭遇するであろう MVC 例外 を参照ください。

- disableOutputBuffering は、 アクションコントローラの出力をバッファリングしないよう ディスパッチャ に指示します。デフォルトでは、 ディスパッチャがいったんすべての出力をキャプチャして、 レスポンスオブジェクトに追加しています。

- noViewRenderer を使用して、ViewRenderer を無効にします。このパラメータを `TRUE` に設定すると、無効となります。

- noErrorHandler を使用して、 エラーハンドラプラグイン を無効にします。 このパラメータを `TRUE` に設定すると、無効となります。

## フロントコントローラの継承

フロントコントローラを継承する際には、 最低限 `getInstance()` メソッドをオーバーライドしなければなりません。

    class My_Controller_Front extends Zend_Controller_Front
    {
        public static function getInstance()
        {
            if (null === self::$_instance) {
                self::$_instance = new self();
            }

            return self::$_instance;
        }
    }

`getInstance()` メソッドをオーバーライドすることで、それ以降の `Zend_Controller_Front::getInstance()` のコールが `Zend_Controller_Front` ではなく新しいサブクラスのインスタンスを返すようになります。 これは、デフォルト以外のルータやビューヘルパーを使用する場合などに便利です。

何か新しい機能 (たとえばプラグインの自動ローダーや、 アクションヘルパーのパスの指定方法) を追加したいというのでもない限り、 ふつうはフロントコントローラのサブクラスを作成する必要はありません。 ほかに変更したくなるような箇所としては、 コントローラディレクトリの保存方法や デフォルトルータ/デフォルトディスパッチャを使用するかどうかなどがあるでしょう。