コントローラ ========== `コントローラ` は [CController] か、[CController] を拡張したクラスのインスタンスです。 コントローラは、ユーザが要求した時にアプリケーションオブジェクトにより生成されます。 コントローラは、起動されると、要求されたアクションを実行するために、通常は、必要なモデルを取り込んで適切なビューを表示します。 `アクション` は、最も単純化された形式としては、コントローラクラスの `action` で始まる名前のメソッドです。 コントローラは既定のアクション (デフォルトアクション) を持っています。 どのアクションを実行するかをユーザが指定しない場合、デフォルトアクションが実行されます。 既定では、デフォルトアクション名は `index` です。 これは、インスタンスのパブリック変数 [CController::defaultAction] を設定することで変更できます。 以下のコードは `site` コントローラと `index` アクション (デフォルトのアクション) と `contact` アクションを定義します。 ~~~ [php] class SiteController extends CController { public function actionIndex() { // ... } public function actionContact() { // ... } } ~~~ ルート (経路) ----- コントローラとアクションは ID により識別されます。 コントローラ ID は `path/to/xyz` の形式で、コントローラクラスファイル `protected/controllers/path/to/XyzController.php` に対応します。 (`xyz` を実際の名前に置き換えて考えてください。例えば、`post` は `protected/controllers/PostController.php` に対応します。) また、アクション ID はアクションメソッド名からプレフィックス `action` を除いたものです。 たとえば、コントローラクラスに `actionEdit` という名前のメソッドがあれば、アクション ID は `edit` になります。 ユーザはルート (経路) により、特定のコントローラとアクションをリクエストします。 ルートはスラッシュによりコントローラ ID とアクション ID を連結することで形成されます。 たとえば、ルート `post/edit` は `PostController` の `edit` アクションを参照します。 そして、デフォルトでは `http://hostname/index.php?r=post/edit` という URL が post コントローラと edit アクションをリクエストするものになります。 >Note|注意: デフォルトでは、ルートは大文字と小文字を区別します。 >アプリケーション初期構成で [CUrlManager::caseSensitive] を false に設定することで、大文字と小文字を区別しないようすることも可能です。 >大文字と小文字を区別しないモード (case-insensitive mode) の場合は、 >コントローラクラスファイルを含むディレクトリ名が小文字であること、さらに、 >[controller map|CWebApplication::controllerMap] と >[action map|CController::actions] の両方でキーが小文字であることという規約を必ず守って下さい。 アプリケーションは [モジュール](/doc/guide/basics.module) を含むことができます。 モジュール内のコントローラのアクションは `moduleID/controllerID/actionID` のフォーマットで表されます。 より詳細には [モジュール](/doc/guide/basics.module) の章を見てください。 コントローラのインスタンス ------------------------ コントローラのインスタンスは [CWebApplication] が入ってきたリクエストを処理する際に生成されます。 コントローラ ID が与えられると、アプリケーションは次のルールを用いて、コントローラクラスとクラスファイルを探し出します。 - [CWebApplication::catchAllRequest] が指定されている場合、コントローラはこのプロパティを元に生成され、 ユーザの指定したコントローラ ID は無視されます。 これは主にアプリケーションをメンテナンスモードにし、通知のための静的ページを表示するために使用します。 - ID が [CWebApplication::controllerMap] に指定されている場合、 対応するコントローラ設定に基づき、コントローラインスタンスが生成されます。 - ID が `'path/to/xyz'` 形式の場合、コントローラクラス名は `XyzController` で、 対応するクラスファイルは `protected/controllers/path/to/XyzController.php` であると仮定されます。 たとえば、コントローラ ID が `admin/user` なら、コントローラクラス名が `UserController` で、 クラスファイルが `protected/controllers/admin/UserController.php` になります。 もしクラスファイルがなければ、404 [CHttpException] が呼び出されます。 [モジュール](/doc/guide/basics.module) が使われる場合には、上記のプロセスは若干異ります。 具体的には、アプリケーションは ID がモジュール中のコントローラを参照しているかを調べ、 もしそうなら、モジュールインスタンスが最初に生成されコントローラインスタンスが次に生成されます。 アクション ------ 前述したとおり、アクションは `action` から始まる名前のメソッドにより定義できます。 より高度な方法は、アクションクラスを定義し、リクエスト時にインスタンス化するようにコントローラに要求する方法です。 この方法を用いる事で、アクションの再利用が可能になるため、より再利用性を高められます。 新しいアクションクラスを定義するためには、下記のように行います: ~~~ [php] class UpdateAction extends CAction { public function run() { // ここにアクションロジックを記述 } } ~~~ コントローラがこのアクションを認識するように、このコントローラクラスの [actions()|CController::actions] メソッドを上書き定義します。 ~~~ [php] class PostController extends CController { public function actions() { return array( 'edit'=>'application.controllers.post.UpdateAction', ); } } ~~~ 上記で使用されている、`application.controllers.post.UpdateAction` というパスは、 アクションクラスファイル `protected/controllers/post/UpdateAction.php` へのパスエイリアスです。 クラスベースのアクションを書く事で、モジュール方式でアプリケーションを構成出来ます。 たとえば、コントローラのためのコードを構成するために、次のようなディレクトリ構造を利用出来ます。: ~~~ protected/ controllers/ PostController.php UserController.php post/ CreateAction.php ReadAction.php UpdateAction.php user/ CreateAction.php ListAction.php ProfileAction.php UpdateAction.php ~~~ ### アクションパラメータ結合 バージョン 1.1.4 からは自動アクションパラメータ結合がサポートされました。 これは、コントローラアクションメソッドにおいて名前付きパラメータを定義し、その値が自動的に $_GET から代入されるものです。 これがどのように動作するかを説明するために、`PostController` コントローラの `create` アクションを記述することを考えてみましょう。 このアクションは二つのパラメータを必要とします。 * `category`: カテゴリ ID を意味する整数で、この元で新規ポストが作成されます。 * `language`: 新規ポストが書かれる言語コードを意味する文字列です。 必要なパラメータ値を `$_GET` から取得するために以下のようなつまらないコードを書くはめになるかもしれません。 ~~~ [php] class PostController extends CController { public function actionCreate() { if(isset($_GET['category'])) $category=(int)$_GET['category']; else throw new CHttpException(404,'invalid request'); if(isset($_GET['language'])) $language=$_GET['language']; else $language='en'; // ... 面白いコードはここから開始 ... } } ~~~ さて、アクションパラメータ機能を用いると、タスクがもっと楽しいものになります。 ~~~ [php] class PostController extends CController { public function actionCreate($category, $language='en') { $category=(int)$category; // ... 面白いコードはここから開始 ... } } ~~~ 二つのパラメータをアクションメソッド `actionCreate` に追加したことに注意してください。 パラメータの名前は `$_GET` から得られるパラメータと全く同じにする必要があります。 `$language` パラメータは、リクエストがそういうパラメータを含んでいない場合は、デフォルト値 `en` を取ります。 一方 `$category` はデフォルト値が無いため、もしリクエストが `category` パラメータを含んでいない場合は、 [CHttpException] (error code 400) エラーが自動的に発行されます。 バージョン 1.1.5 からは、配列タイプのアクションパラメータをサポートします。 これは PHP のタイプヒンティングを利用しており、以下のような文法により行われます。 ~~~ [php] class PostController extends CController { public function actionCreate(array $categories) { // Yii は必ず $categories を配列にします } } ~~~ すなわち、メソッドのパラメータ宣言において、`$categories` の直前に `array` キーワードを置きます。 こうすることによって、`$_GET['categories']` が単純な文字列である場合には、その文字列からなる配列に変換されます。 > Note|注意: もしパラメータが `array` タイプヒント無しに宣言された場合は、そのパラメータはスカラー (配列でない) でなくてはいけません。 > この場合、`$_GET` から配列パラメータが渡されると、HTTP 例外が発生します。 バージョン 1.1.7 からは、自動パラメータ結合はクラスベースのアクションにも適用されます。 もしアクションクラス `run()` メソッドがパラメータ付きで定義された場合、それらのパラメータには 対応する名前のリクエストパラメータが代入されます。例えば、 ~~~ [php] class UpdateAction extends CAction { public function run($id) { // $id には $_GET['id'] が代入される } } ~~~ フィルタ ------ フィルタは、コントローラのアクション実行の前か後 (もしくはその両方) に実行されるように構成されるコードの断片です。 たとえば、アクセスコントロールフィルタは、ユーザがリクエストしたアクションを実行する前に、 認証済みである事を確実にするために使用されるかもしれません。 パフォーマンスフィルタは、アクションの実行所要時間を計測するために使用されるかもしれません。 一つのアクションは複数のフィルタを持つことが出来ます。 フィルタはフィルタリストに登場する順で順次実行されます。 フィルタは、アクションと残りの実行されていないフィルタの実行を防ぐことが出来ます。 フィルタはコントローラクラスメソッドで定義出来ます。 メソッド名は必ず `filter` で始めます。 たとえば、`filterAccessControl` メソッドは、`accessControl` という名前のフィルタを定義します。 フィルタメソッドは正しいシグネチャを持っていなければなりません: ~~~ [php] public function filterAccessControl($filterChain) { // フィルタリングとアクションの実行を継続するために、$filterChain->run() をコールします } ~~~ ここで、`$filterChain` はリクエストされたアクションに結びついているフィルターリストを表した、[CFilterChain] のインスタンスです。 フィルタメソッド内で、フィルタリングとアクションの実行を継続するためには、$filterChain->run() をコールします。 フィルタもまた [CFilter] かその子クラスのインスタンスにする事が出来ます。 次のコードは新しいフィルタクラスを定義するものです: ~~~ [php] class PerformanceFilter extends CFilter { protected function preFilter($filterChain) { // アクションが実行される前に実行されるコード return true; // アクションが実行されるべきでない場合は false } protected function postFilter($filterChain) { // アクションが実行された後に実行されるコード } } ~~~ アクションにフィルタを適用するために、`CController::filters()` メソッドを上書きする必要があります。 このメソッドはフィルタ構成の配列を返さなくてはなりません。たとえば、 ~~~ [php] class PostController extends CController { ...... public function filters() { return array( 'postOnly + edit, create', array( 'application.filters.PerformanceFilter - edit, create', 'unit'=>'second', ), ); } } ~~~ 上記のコードは `postOnly` と `PerformanceFilter` という、二つのフィルタを指定しています。 `postOnly` フィルタは、メソッドベースのフィルタです (対応するフィルタメソッドは、既に [CController] に定義されています) 。 そして、`PerformanceFilter` フィルタはオブジェクトベースです。 `application.filters.PerformanceFilter` というパスは、フィルタークラスファイル `protected/filters/PerformanceFilter` へのパスのエイリアスです。 フィルタオブジェクトのプロパティ値を初期化するために、配列を使用して `PerformanceFilter` を構成しています。 ここでは、`PerformanceFilter` の `unit` プロパティを `'second'` に初期化しています。 プラスやマイナス演算子を使用すると、アクションに対してのフィルタ適用の有無を指定できます。 上記の場合、`postOnly` は `edit` と `create` アクションに適用され、 `PerformanceFilter` は `edit` と `create` アクション以外のすべてのアクションに適用されます。 もし、プラスとマイナスのどちらも使用されていない場合、フィルタはすべてのアクションに適用されます。
$Id$