4.3. サブクラス化

4.3.1. 導入

Zend_Controller システムは、拡張性を考慮して作成されています。 拡張方法としては、既存のクラスを継承する以外にも Zend_Controller_Router_Interface および Zend_Controller_Dispatcher_Interface を実装した新しいクラスを作成する方法があります。

新しいルータやディスパッチャを実装する理由としては、 以下のようなものが考えられるでしょう。

  • 既存の URI ルーティングが何らかの理由で適用できない場合。 例えば、既存のウェブサイトと統合する予定であるが、 そこで採用している規約が Zend Framework のルーティング機構と合致しないなど。

  • まったく別のルーティングを実装しなければならない場合。 Zend_Controller_Router クラスは URI のみを対象としています。 例えばコンソールアプリケーションなどの別の形式のプログラムにも MVC パターンを使用したくなることもあるでしょう。 コンソールアプリケーションの場合なら、 独自のルータを実装してコマンドライン引数によるルーティングを行うことになるでしょう。

  • Zend_Controller_Dispatcher の提供する機能がうまく当てはまらない場合。 デフォルト設定では、コントローラはクラスであり、 アクションはクラスのメソッドであることを前提としています。 しかし、これ以外にもいろいろなやり方があるでしょう。 例えば、ディレクトリをコントローラ、 ディレクトリ内のファイルをアクションと考えることもできます。

  • すべてのコントローラで使用するための機能を追加したい場合。 例えば、デフォルトでは Zend_Controller_ActionZend_View と統合されていません。しかし、 コントローラを継承したクラスでこの機能を持たせることができます。 そうすれば、もとの Zend_Controller_RouterZend_Controller_Dispatcher に手を加えずにすみます。

システムの主要な部分、特にディスパッチャをオーバーライドする際には十分注意してください。 Zend_Controller を使用する利点のひとつに、 共通の規約に従ったアプリケーションを作成できるということがあります。 デフォルトの振る舞いをあまりにも変更してしまうと、この利点が失われてしまいます。 しかしながら、世の中にはさまざまな需要があるわけですし、 ひとつのソリューションですべて解決できるわけがありません。 そのために、必要なら変更できるようにしています。

4.3.2. 規約

Zend_Controller のクラスを継承する場合は、 その命名や保存場所はできるだけ次の規約に従うようにしてください。 そうすることで、Zend Framework になじみのある他のプログラマが、 あなたのプロジェクトの内容を簡単に理解できるようになります。

4.3.2.1. プレフィックス

Zend Framework に含まれるクラスの名前は、すべて "Zend_" で始まっています。これがプレフィックスです。 あなたが作成するクラスも同じようにしておくことを推奨します。 例えば、もしあなたの所属する会社名が Widget, Inc. なら、 プレフィックスを "Widget_" とします。

4.3.2.2. ディレクトリ構成

Zend_Controller クラスは、 ライブラリディレクトリに以下のように格納されます。

/library
  /Zend
    /Controller
      Action.php
      Dispatcher.php
      Router.php

Zend_Controller クラスを継承する場合は、 作成したクラスを (あなたのプレフィックスのもとで) 同じ構造で格納することを推奨します。こうすることで、 あなたのプロジェクトのコードをレビューする人たちがそれを見つけやすくなるでしょう。

例えば Widget, Inc. のプロジェクトがルータのみを独自に実装した場合は、 次のようになるでしょう。

/library
  /Zend
  /Widget
    /Controller
      Router.php
      README.txt

この例では、Widget/Controller/ ディレクトリが Zend/Controller/ ディレクトリと同じ構造になっていることに注意しましょう。 この場合、このファイルでは Widget_Controller_Router というクラスが提供されます。このクラスは Zend_Controller_Router を継承したものか、あるいは Zend_Controller_Router_Interface を実装した独自のクラスとなります。

また、上の例で Widget/Controller/ ディレクトリに README.txt があることにも注意してください。 あなたのプロジェクトを顧客に提供する際には、 それに関する単体テストや文書も作成するよう、Zend は強く推奨します。 そこまではいかなくても、同じディレクトリにシンプルな README.txt ファイルを置き、 変更点や動作内容を簡単に説明しておくようにしましょう。

4.3.3. ルータのインターフェイス

Zend_Controller_Router_Interface で定義されているメソッドは 1 つだけです。

<?php
				
  /**				
   * @param  Zend_Controller_Dispatcher_Interface
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Dispatcher_Token|boolean
   */
  public function route(Zend_Controller_Dispatcher_Interface $dispatcher);

?>

ルーティングが発生するのは、システムが最初にリクエストを受け取ったときだけです。 ルータの役割は、Zend_Controller_Dispatch_Token を生成してコントローラとアクションを指定することです。 その後、これをディスパッチャに渡します。 もしディスパッチトークンが作成できなかった場合は FALSE を返さなければなりません。

ディスパッチャの中には、生成されたディスパッチトークンが本当にディスパッチ可能なのかを、 動的な要因をもとにして事前に調べるものもあります。 このため、ルータは、route() の唯一の引数としてディスパッチャのオブジェクトハンドルを受け取ります。 ディスパッチャは、これを確かめるためのメソッドとして isDispatchable() を提供しています。

4.3.4. ディスパッチャのインターフェイス

Zend_Controller_Front は、 まず最初にルータをコールしてディスパッチトークンを取得し、 それをディスパッチャに渡します。ディスパッチャはアクションを起動 (コントローラのインスタンスを作成し、そのアクションをコール) し、論理型の FALSE あるいは別のディスパッチトークンのどちらかを返します。

Zend_Controller_Front は、 ディスパッチトークンが返されなくなるまで、 ディスパッチャを繰り返しコールします。これが、いわゆるディスパッチループです。 これにより、アクションが処理を順にすすめていけるようになります。

Zend_Controller_Dispatcher_Interface では 2 つのメソッドが定義されています。

<?php
				
/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return boolean
 */
public function isDispatchable(Zend_Controller_Dispatcher_Token $route);

?>

isDispatchable() は、 ディスパッチトークンが実際にディスパッチ可能かどうかを調べます。 ディスパッチ可能な場合に TRUE、それ以外の場合に FALSE を返します。ディスパッチ可能かどうかを判断する基準は、 インターフェリスを実装したクラスで記述します。 デフォルトの実装である Zend_Controller_Dispatcher では、 コントローラのファイルが存在するかどうか、 そのファイルでクラスが定義されているかどうか、 そしてそのクラスでアクションメソッドが定義されているかどうかを調べます。

<?php
			
/**
 * @param  Zend_Controller_Dispatcher_Token $route
 * @return Zend_Controller_Dispatcher_Token|boolean
 */
public function dispatch(Zend_Controller_Dispatcher_Token $route);

?>

dispatch() は、実際の処理を行うところです。 このメソッドは、コントローラのアクションを実行しなければなりません。 また、ディスパッチトークンあるいは論理型の FALSE のいずれかを返さなければなりません。 FALSE は、この後でやるべきことがもうないということを意味します。