導入
Zend_Controller_Action は、
モデル - ビュー - コントローラ (MVC)
パターンにもとづいたウェブアプリケーションを作成する際に、
フロントコントローラで使用するアクションコントローラを実装するための抽象クラスです。
Zend_Controller_Action を使用するには、
実際のアクションコントローラクラス内でこのクラスのサブクラスを作成する必要があります
(あるいは、作成したサブクラスをもとにしてアクションコントローラを作成します)。
基本的な使い方としては、まずサブクラスを作成し、
そしてあなたのサイト上で処理したいさまざまなアクションに対応する
アクションメソッドを作成するという流れになります。
Zend_Controller は、このクラス内のメソッドで 'Action'
という名前で終わるものを見つけると、
ルーティングやディスパッチの際にそれらを自動的にアクションとして扱います。
たとえば、次のようなクラスを見てみましょう。
class FooController extends Zend_Controller_Action
{
public function barAction()
{
// 何かをします
}
public function bazAction()
{
// 何かをします
}
}
この FooController クラス (foo コントローラ)
では、ふたつのアクション bar および baz
が定義されています。
もちろんこれ以外にもたくさんの機能があります。
たとえば初期化アクションを独自に作成したり、
アクションを指定しなかった (あるいは無効なアクションを指定した)
際にコールされるデフォルトのアクションを指定したり、
ディスパッチの前後に実行されるフックを指定したり、
さまざまなヘルパーメソッドを使用したりといったことができます。
この章では、アクションコントローラの機能の概要を説明します。
Note: デフォルトの挙動
デフォルトでは、フロントコントローラ
は ViewRenderer
アクションヘルパーを有効にします。このヘルパーは、
ビューオブジェクトをコントローラに注入し、
ビューを自動的にレンダリングします。
アクションコントローラでこれを無効にするには、
以下のいずれかの方法を使用します。
class FooController extends Zend_Controller_Action
{
public function init()
{
// このコントローラでのみ無効にします。
// 初期化時に読み込まれるので、全アクションに影響を及ぼします
$this->_helper->viewRenderer->setNoRender(true);
// 全体で無効にします
$this->_helper->removeHelper('viewRenderer');
// これも全体で無効にしますが、同時にローカルでも無効にしておく必要があります。
// これは、ローカルの設定を全体に伝播させる方法です。
Zend_Controller_Front::getInstance()
->setParam('noViewRenderer', true);
}
}
initView()、 getViewScript()、
render() および renderScript()
は、それぞれ ViewRenderer へのプロキシとなります。
ただしヘルパーブローカ内にこのヘルパーが登録されていない場合や
noViewRenderer フラグが設定されている場合は除きます。
個々のビューのレンダリングを無効にするには、単純に
ViewRenderer の noRender
フラグを設定することもできます。
class FooController extends Zend_Controller_Action
{
public function barAction()
{
// このアクションでのみ自動レンダリングを無効にします
$this->_helper->viewRenderer->setNoRender();
}
}
ViewRenderer を無効にする場面として考えられるのは、
ビューオブジェクトを必要としない場合や
ビュースクリプト経由でのレンダリングを行わない場合
(たとえば、アクションコントローラを使用して SOAP や
XML-RPC、REST
といったウェブサービスプロトコルを扱う場合)
です。ViewRenderer をグローバルで無効にすることはまずないでしょう。
無効にするとすれば、個々のコントローラやアクション単位で行うことになります。
オブジェクトの初期化
アクションコントローラのコンストラクタをオーバーライドすることもできますが、
お勧めしません。 Zend_Controller_Action::__construct()
は、リクエストオブジェクトやレスポンスオブジェクトを登録するなどの重要な作業を行います。
また、フロントコントローラから渡された起動時引数の処理も行います。
コンストラクタをオーバーライドする場合は、必ずその中で
parent::__construct($request, $response, $invokeArgs)
をコールするようにしましょう。
初期化作業をカスタマイズするには、コンストラクタをオーバーライドするよりも
init() メソッドを使うほうがお勧めです。
これは、 __construct()
の中で最後にコールされます。たとえば、
初期化時にデータベースに接続したいなら次のようにします。
class FooController extends Zend_Controller_Action
{
public function init()
{
$this->
db = Zend_Db::
factory('Pdo_Mysql',
array(
'host' => 'myhost',
'username' => 'user',
'password' => 'XXXXXXX',
'dbname' => 'website'
));
}
}
ディスパッチ前後のフック
Zend_Controller_Action には、
リクエストされたアクションの前後にコールされるふたつのメソッドがあります。それが
preDispatch() と postDispatch() です。
これらはさまざまな場面で活用できます。
たとえばアクションを実行する前に認証情報や ACL
を調べたり ( preDispatch() の中で
_forward() をコールすると、
そのアクションの処理は飛ばされます)、
作成したコンテンツを ( postDispatch() で)
全サイト共通のテンプレートに配置したりといったことが考えられます。
Note: init() 対 preDispatch() の使用法
前述のセクションでは、
init() メソッドを紹介しましたが、
このセクションでは preDispatch() メソッドを紹介します。
それらの違いは何で、それぞれどのような動作をさせるでしょう?
init() メソッドは、主にコンストラクタを拡張することが目的です。
一般的に、コンストラクタはオブジェクトの状態を単純に設定しなければならなくて、
多くのロジックを実行してはいけません。
これは、コントローラで使用されるリソース(例えばモデル、オブジェクトの構成、その他)を初期化すること、
またはフロントコントローラ、ブートストラップ、
またはレジストリから取得される値をアサインすることを含むかもしれません。
preDispatch() メソッドは、
オブジェクトまたは環境(例えば、ビュー、アクション・ヘルパー、その他)
の状態を設定するために使うこともできます。
しかし、その主な目的は、リクエストされたアクションをディスパッチしなければいけないかどうか
決定することです。
もししないのならば、別のアクションに _forward() するか、
または例外を投げなければなりません。
注意: init() から実行されると、
実は _forward() は正しく動作しません。
それは、2つのメソッドの意図が形になったものです。
ビューの統合
Note: デフォルトのビュー統合は ViewRenderer を介します
この節の内容は、
ViewRenderer
を明示的に無効にしている場合にのみ有効です。
それ以外の場合はこの節を読み飛ばしてください。
Zend_Controller_Action では、
ビューの統合のためのちょっとした柔軟な仕組みを提供しています。
これを行うのは initView() と render()
のふたつのメソッドです。前者のメソッドはパブリックプロパティ
$view の遅延読み込みを行い、
後者のメソッドはアクションの要求にもとづいてビューをレンダリングします。
その際に、ディレクトリ階層をもとにスクリプトのパスを決定します。
ビューの初期化
initView() はビューオブジェクトを初期化します。
render() は initView()
をコールしてビューオブジェクトを取得しますが、
その初期化はいつでも好きなときに行うことができます。
デフォルトでは、取得した結果は Zend_View
オブジェクトのプロパティ $view に格納されますが、
Zend_View_Interface を実装したクラスなら何でも好きなものを使用できます。
$view がすでに初期化されている場合は、そのプロパティの内容を返します。
デフォルトの実装は、以下のようなディレクトリ階層を前提としています。
applicationOrModule/
controllers/
IndexController.php
views/
scripts/
index/
index.phtml
helpers/
filters/
言い換えると、ビュースクリプトが
/views/scripts/ ディレクトリ内にあり、かつ
/views/ ディレクトリ内の同一階層に各機能
(ヘルパー、フィルタ)のディレクトリがあるということです。
ビュースクリプトの名前とパスを決定する際の基底ディレクトリとして
/views/scripts/ が用いられます。
その中に、ビュースクリプトを実行するコントローラ名に基づいた名前のディレクトリが作成されます。
ビューのレンダリング
render() のシグネチャは次のとおりです。
string render(string $action = null,
string $name = null,
bool $noController = false);
render() はビュースクリプトをレンダリングします。
引数を省略した場合は、[controller]/[action].phtml
が指定されたものとみなします(.phtml
は $viewSuffix プロパティの値です)。
$action を指定すると、/[controller]/
ディレクトリにあるその名前のテンプレートをレンダリングします。
/[controller]/ ディレクトリを使用しないようにするには、
$noController に TRUE を指定します。
テンプレートをレンダリングした結果はレスポンスオブジェクトに格納されます。
レスポンスオブジェクトの中の、
特定の名前をつけた部分 に格納したい場合は、
$name の値を指定します。
Note:
コントローラやアクションの名前には区切り文字
('_' や '.'、'-') を含めることができるので、
render() はスクリプト名を決定する際にこれらの文字を
'-' に正規化します。内部的には、
ディスパッチャで設定されている単語やパスの区切り文字を正規化時に用います。
したがって、/foo.bar/baz-bat へのリクエストの際に
レンダリングされるスクリプトは foo-bar/baz-bat.phtml です。
アクションメソッド名が camelCase 方式の場合、
ビュースクリプトのファイル名では単語が '-' で区切られることに注意しましょう。
例を見てみましょう。
class MyController extends Zend_Controller_Action
{
public function fooAction()
{
// my/foo.phtml をレンダリングします
$this->render();
// my/bar.phtml をレンダリングします
$this->render('bar');
// baz.phtml をレンダリングします
$this->render('baz', null, true);
// my/login.phtml をレンダリングし、
// レスポンスオブジェクトの 'form' の部分に返します
$this->render('login', 'form');
// site.phtml をレンダリングし、レスポンスオブジェクトの
// 'page' の部分に返します
// 'my/' ディレクトリは使用しません
$this->render('site', 'page', true);
}
public function bazBatAction()
{
// my/baz-bat.phtml をレンダリングします
$this->render();
}
}
アクションコントローラのサブクラスの作成
アクションコントローラを作成するには、必ず
Zend_Controller_Action のサブクラスを作成しなければならないようになっています。
最低限、コントローラがコールするアクションメソッドを定義しなければなりません。
自分のウェブアプリケーション用に便利な機能を実装していく一方で、
同じような前処理やちょっとした処理をあちこちのコントローラで書いているといったことはありませんか?
そのような場合は、Zend_Controller_Action
を継承した共通基底コントローラクラスを作成し、
共通処理をそこにまとめていくようにしましょう。
コントローラへのリクエストの際に未定義のアクションメソッドが指定された場合は、
Zend_Controller_Action::__call() を実行します。
__call() とはもちろん、PHP
のマジックメソッドで、メソッドのオーバーロード用に使用するものです。
デフォルトでは、このメソッドは
Zend_Controller_Action_Exception
をスローして、コントローラの中にアクションが見つからなかったことを示します。
メソッド名の最後が 'Action' であった場合は、
おそらく存在しないアクションをリクエストしたのであろうとみなします。
そして、コード 404 で例外を返します。その他のメソッドの場合は
コード 500 で例外を返します。
これにより、単にページが見つからないだけなのか
アプリケーションエラーなのかをエラーハンドラで区別できるようになります。
もし別の動作をさせたい場合は、これをオーバーライドしましょう。
たとえば、エラーメッセージを表示させたい場合は次のようになります。
class MyController extends Zend_Controller_Action
{
public function __call($method, $args)
{
if ('Action' ==
substr($method,
-6)) {
// アクションメソッドが見つからなかった場合は、
// エラー用のテンプレートをレンダリングします
return $this->render('error');
}
// その他のメソッドの場合は例外をスローします
throw new Exception('Invalid method "'
. $method
. '" called',
500);
}
}
もうひとつの例として、デフォルトコントローラに転送する処理を見てみましょう。
class MyController extends Zend_Controller_Action
{
public function indexAction()
{
$this->render();
}
public function __call($method, $args)
{
if ('Action' ==
substr($method,
-6)) {
// アクションメソッドが見つからなかった場合は、
// index アクションに転送します
return $this->_forward('index');
}
// その他のメソッドの場合は例外をスローします
throw new Exception('Invalid method "'
. $method
. '" called',
500);
}
}
__call() をオーバーライドするかわりに、
これまで説明してきた各種フックメソッドをオーバーライドしてコントローラをカスタマイズすることもできます。
たとえば、ビューオブジェクトをレジストリに保存したい場合は、
initView() メソッドを次のように書き換えることになるでしょう。
abstract class My_Base_Controller extends Zend_Controller_Action
{
public function initView()
{
if (null === $this->view) {
if (Zend_Registry::isRegistered('view')) {
$this->view = Zend_Registry::get('view');
} else {
$this->view = new Zend_View();
$this->
view->
setBasePath(dirname(__FILE__) .
'/../views');
}
}
return $this->view;
}
}
この章の情報をもとに、それぞれの機能の柔軟性をもとにして
アプリケーションやサイトの要求に応じたコントローラを作成していくとよいでしょう。