ディスパッチャ

概要

ディスパッチ処理は、リクエストオブジェクトである Zend_Controller_Request_Abstract を受け取り、 そこに含まれる情報 (モジュール名、コントローラ名、アクション名およびオプションのパラメータ) を展開し、コントローラのインスタンスを作成してそのコントローラのアクションをコールします。 モジュールやコントローラ、アクションが見つからない場合は、 デフォルト値を使用します。Zend_Controller_Dispatcher_Standard では、コントローラとアクションのデフォルトはどちらも index で、モジュールのデフォルトは default です。しかし、 setDefaultController() メソッドや setDefaultAction() メソッド、そして setDefaultModule() でこれらを変更することもできます。

デフォルトモジュール

モジュール構造のアプリケーションを作成する場合に、 デフォルトのモジュールにも名前空間を定義したくなることもあるでしょう (デフォルトの設定では、デフォルトモジュールには名前空間が ありません)。1.5.0 以降では、 フロントコントローラあるいはディスパッチャで prefixDefaultModuleTRUE を設定すればこれが実現できるようになりました。

// フロントコントローラで
$front->setParam('prefixDefaultModule', true);

// ディスパッチャで
$dispatcher->setParam('prefixDefaultModule', true);

これにより、既存のモジュールをアプリケーションのデフォルトモジュールとすることができます。

ディスパッチ処理が発生するのは、フロントコントローラでのループの内部です。 ディスパッチ処理を行う前に、フロントコントローラはルーティングを行い、 ユーザが指定したコントローラとアクション、そして追加のパラメータを取得します。 それからディスパッチループに入り、リクエストを配送します。

ループ内では、まず最初にリクエストオブジェクトのフラグを設定します。 このフラグは、アクションがディスパッチされたことを示すものです。 アクション内や pre/postDispatch プラグインでこのフラグをリセットすると、 ディスパッチループがそのまま継続され、もう一度リクエストを処理しようとします。 リクエスト内のコントローラやアクションを変更してフラグをリセットすることで、 さまざまなリクエストを続けて実行させることができます。

このようなディスパッチ処理を制御する アクションコントローラのメソッドが _forward() です。 このメソッドを preDispatch()postDispatch() やアクションメソッドでコールし、 コントローラやアクション、 そして新しいアクションに送りたい追加のパラメータを指定します。

public function fooAction()
{
    // 現在のモジュールおよびコントローラの、別のアクションに転送します
    $this->_forward('bar', null, null, array('baz' => 'bogus'));
}

public function barAction()
{
    // 現在のモジュールにある、別のコントローラのアクション
    // FooController::bazAction() に転送します
    $this->_forward('baz', 'foo', null, array('baz' => 'bogus'));
}

public function bazAction()
{
    // 別のモジュールにある、別のコントローラのアクション
    // Foo_BarController::bazAction() に転送します
    $this->_forward('baz', 'bar', 'foo', array('baz' => 'bogus'));
}

ディスパッチャのサブクラスの作成

Zend_Controller_Front は、 まず最初にルータをコールして、 リクエスト内で最初にディスパッチできるアクションを決定します。 その後、ディスパッチャループに入り、ディスパッチャをコールしてアクションを振り分けます。

ディスパッチャが動作するためには、さまざまなデータが必要です。 たとえば、コントローラ名やアクション名を決定する方法、 コントローラクラスを探す場所、モジュール名が有効かどうか、 その他、リクエストの内容をディスパッチするために必要な情報を取得する API が必要となります。

Zend_Controller_Dispatcher_Interface では次のようなメソッドを定義しています。ディスパッチャは、これを実装しなければなりません。

interface Zend_Controller_Dispatcher_Interface
{
    /**
     * Format a string into a controller class name.
     *
     * @param string $unformatted
     * @return string
     */
    public function formatControllerName($unformatted);

    /**
     * Format a string into an action method name.
     *
     * @param string $unformatted
     * @return string
     */
    public function formatActionName($unformatted);

    /**
     * Determine if a request is dispatchable
     *
     * @param  Zend_Controller_Request_Abstract $request
     * @return boolean
     */
    public function isDispatchable(
        Zend_Controller_Request_Abstract $request
    );

    /**
     * Set a user parameter (via front controller, or for local use)
     *
     * @param string $name
     * @param mixed $value
     * @return Zend_Controller_Dispatcher_Interface
     */
    public function setParam($name, $value);

    /**
     * Set an array of user parameters
     *
     * @param array $params
     * @return Zend_Controller_Dispatcher_Interface
     */
    public function setParams(array $params);

    /**
     * Retrieve a single user parameter
     *
     * @param string $name
     * @return mixed
     */
    public function getParam($name);

    /**
     * Retrieve all user parameters
     *
     * @return array
     */
    public function getParams();

    /**
     * Clear the user parameter stack, or a single user parameter
     *
     * @param null|string|array single key or array of keys for
     *        params to clear
     * @return Zend_Controller_Dispatcher_Interface
     */
    public function clearParams($name = null);

    /**
     * Set the response object to use, if any
     *
     * @param Zend_Controller_Response_Abstract|null $response
     * @return void
     */
    public function setResponse(
        Zend_Controller_Response_Abstract $response = null
    );

    /**
     * Retrieve the response object, if any
     *
     * @return Zend_Controller_Response_Abstract|null
     */
    public function getResponse();

    /**
     * Add a controller directory to the controller directory stack
     *
     * @param string $path
     * @param string $args
     * @return Zend_Controller_Dispatcher_Interface
     */
    public function addControllerDirectory($path, $args = null);

    /**
     * Set the directory (or directories) where controller files are
     * stored
     *
     * @param string|array $dir
     * @return Zend_Controller_Dispatcher_Interface
     */
    public function setControllerDirectory($path);

    /**
     * Return the currently set directory(ies) for controller file
     * lookup
     *
     * @return array
     */
    public function getControllerDirectory();

    /**
     * Dispatch a request to a (module/)controller/action.
     *
     * @param  Zend_Controller_Request_Abstract $request
     * @param  Zend_Controller_Response_Abstract $response
     * @return Zend_Controller_Request_Abstract|boolean
     */
    public function dispatch(
        Zend_Controller_Request_Abstract $request,
        Zend_Controller_Response_Abstract $response
    );

    /**
     * Whether or not a given module is valid
     *
     * @param string $module
     * @return boolean
     */
    public function isValidModule($module);

    /**
     * Retrieve the default module name
     *
     * @return string
     */
    public function getDefaultModule();

    /**
     * Retrieve the default controller name
     *
     * @return string
     */
    public function getDefaultControllerName();

    /**
     * Retrieve the default action
     *
     * @return string
     */
    public function getDefaultAction();
}

しかし、たいていの場合は単純に抽象クラス Zend_Controller_Dispatcher_Abstract を継承するだけで事足りるでしょう。ここには、これらのメソッドがすでに定義されています。 あるいは、Zend_Controller_Dispatcher_Standard を継承して、標準の機能と異なる部分だけを変更するということも可能です。

ディスパッチャのサブクラスを作成する必要がある場面としては、 たとえばアクションコントローラ内で 標準とは異なるクラス名やメソッド名の命名規則を使用したいなどということが考えられます。 あるいは、クラスメソッドに振り分けるのではなく コントローラディレクトリは以下のアクションファイルに振り分けるなど、 異なるディスパッチ方式を使用したい場合にもサブクラスを作成する必要があります。