コントローラ

コントローラCController か、CController を拡張したクラスのインスタンスです。 コントローラは、ユーザが要求した時にアプリケーションオブジェクトにより生成されます。 コントローラは、起動されると、要求されたアクションを実行するために、通常は、必要なモデルを取り込んで適切なビューを表示します。 アクション は、最も単純化された形式としては、コントローラクラスの action で始まる名前のメソッドです。

コントローラは既定のアクション (デフォルトアクション) を持っています。 どのアクションを実行するかをユーザが指定しない場合、デフォルトアクションが実行されます。 既定では、デフォルトアクション名は index です。 これは、インスタンスのパブリック変数 CController::defaultAction を設定することで変更できます。

以下のコードは site コントローラと index アクション (デフォルトのアクション) と contact アクションを定義します。

class SiteController extends CController
{
    public function actionIndex()
    {
        // ...
    }
 
    public function actionContact()
    {
        // ...
    }
}

1. ルート (経路)

コントローラとアクションは ID により識別されます。 コントローラ ID は path/to/xyz の形式で、コントローラクラスファイル protected/controllers/path/to/XyzController.php に対応します。 (xyz を実際の名前に置き換えて考えてください。例えば、postprotected/controllers/PostController.php に対応します。) また、アクション ID はアクションメソッド名からプレフィックス action を除いたものです。 たとえば、コントローラクラスに actionEdit という名前のメソッドがあれば、アクション ID は edit になります。

ユーザはルート (経路) により、特定のコントローラとアクションをリクエストします。 ルートはスラッシュによりコントローラ ID とアクション ID を連結することで形成されます。 たとえば、ルート post/editPostControlleredit アクションを参照します。 そして、デフォルトでは http://hostname/index.php?r=post/edit という URL が post コントローラと edit アクションをリクエストするものになります。

注意: デフォルトでは、ルートは大文字と小文字を区別します。 アプリケーション初期構成で CUrlManager::caseSensitive を false に設定することで、大文字と小文字を区別しないようすることも可能です。 大文字と小文字を区別しないモード (case-insensitive mode) の場合は、 コントローラクラスファイルを含むディレクトリ名が小文字であること、さらに、 controller mapaction map の両方でキーが小文字であることという規約を必ず守って下さい。

アプリケーションは モジュール を含むことができます。 モジュール内のコントローラのアクションは moduleID/controllerID/actionID のフォーマットで表されます。 より詳細には モジュール の章を見てください。

2. コントローラのインスタンス

コントローラのインスタンスは 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 が呼び出されます。

モジュール が使われる場合には、上記のプロセスは若干異ります。 具体的には、アプリケーションは ID がモジュール中のコントローラを参照しているかを調べ、 もしそうなら、モジュールインスタンスが最初に生成されコントローラインスタンスが次に生成されます。

3. アクション

前述したとおり、アクションは action から始まる名前のメソッドにより定義できます。 より高度な方法は、アクションクラスを定義し、リクエスト時にインスタンス化するようにコントローラに要求する方法です。 この方法を用いる事で、アクションの再利用が可能になるため、より再利用性を高められます。

新しいアクションクラスを定義するためには、下記のように行います:

class UpdateAction extends CAction
{
    public function run()
    {
        // ここにアクションロジックを記述
    }
}

コントローラがこのアクションを認識するように、このコントローラクラスの actions() メソッドを上書き定義します。

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 から取得するために以下のようなつまらないコードを書くはめになるかもしれません。

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';
 
        // ... 面白いコードはここから開始 ...
    }
}

さて、アクションパラメータ機能を用いると、タスクがもっと楽しいものになります。

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 のタイプヒンティングを利用しており、以下のような文法により行われます。

class PostController extends CController
{
    public function actionCreate(array $categories)
    {
        // Yii は必ず $categories を配列にします
    }
}

すなわち、メソッドのパラメータ宣言において、$categories の直前に array キーワードを置きます。 こうすることによって、$_GET['categories'] が単純な文字列である場合には、その文字列からなる配列に変換されます。

注意: もしパラメータが array タイプヒント無しに宣言された場合は、そのパラメータはスカラー (配列でない) でなくてはいけません。 この場合、$_GET から配列パラメータが渡されると、HTTP 例外が発生します。

バージョン 1.1.7 からは、自動パラメータ結合はクラスベースのアクションにも適用されます。 もしアクションクラス run() メソッドがパラメータ付きで定義された場合、それらのパラメータには 対応する名前のリクエストパラメータが代入されます。例えば、

class UpdateAction extends CAction
{
    public function run($id)
    {
        // $id には $_GET['id'] が代入される
    }
}

4. フィルタ

フィルタは、コントローラのアクション実行の前か後 (もしくはその両方) に実行されるように構成されるコードの断片です。 たとえば、アクセスコントロールフィルタは、ユーザがリクエストしたアクションを実行する前に、 認証済みである事を確実にするために使用されるかもしれません。 パフォーマンスフィルタは、アクションの実行所要時間を計測するために使用されるかもしれません。

一つのアクションは複数のフィルタを持つことが出来ます。 フィルタはフィルタリストに登場する順で順次実行されます。 フィルタは、アクションと残りの実行されていないフィルタの実行を防ぐことが出来ます。

フィルタはコントローラクラスメソッドで定義出来ます。 メソッド名は必ず filter で始めます。 たとえば、filterAccessControl メソッドは、accessControl という名前のフィルタを定義します。 フィルタメソッドは正しいシグネチャを持っていなければなりません:

public function filterAccessControl($filterChain)
{
    // フィルタリングとアクションの実行を継続するために、$filterChain->run() をコールします
}

ここで、$filterChain はリクエストされたアクションに結びついているフィルターリストを表した、CFilterChain のインスタンスです。 フィルタメソッド内で、フィルタリングとアクションの実行を継続するためには、$filterChain->run() をコールします。

フィルタもまた CFilter かその子クラスのインスタンスにする事が出来ます。 次のコードは新しいフィルタクラスを定義するものです:

class PerformanceFilter extends CFilter
{
    protected function preFilter($filterChain)
    {
        // アクションが実行される前に実行されるコード
        return true; // アクションが実行されるべきでない場合は false
    }
 
    protected function postFilter($filterChain)
    {
        // アクションが実行された後に実行されるコード
    }
}

アクションにフィルタを適用するために、CController::filters() メソッドを上書きする必要があります。 このメソッドはフィルタ構成の配列を返さなくてはなりません。たとえば、

class PostController extends CController
{
    ......
    public function filters()
    {
        return array(
            'postOnly + edit, create',
            array(
                'application.filters.PerformanceFilter - edit, create',
                'unit'=>'second',
            ),
        );
    }
}

上記のコードは postOnlyPerformanceFilter という、二つのフィルタを指定しています。 postOnly フィルタは、メソッドベースのフィルタです (対応するフィルタメソッドは、既に CController に定義されています) 。 そして、PerformanceFilter フィルタはオブジェクトベースです。 application.filters.PerformanceFilter というパスは、フィルタークラスファイル protected/filters/PerformanceFilter へのパスのエイリアスです。 フィルタオブジェクトのプロパティ値を初期化するために、配列を使用して PerformanceFilter を構成しています。 ここでは、PerformanceFilterunit プロパティを 'second' に初期化しています。

プラスやマイナス演算子を使用すると、アクションに対してのフィルタ適用の有無を指定できます。 上記の場合、postOnlyeditcreate アクションに適用され、 PerformanceFiltereditcreate アクション以外のすべてのアクションに適用されます。 もし、プラスとマイナスのどちらも使用されていない場合、フィルタはすべてのアクションに適用されます。

$Id$

Be the first person to leave a comment

Please to leave your comment.