エクステンションの作成

エクステンションは第三者の開発者によって使われるはずのものなので、作るためにはさらにいくらかの努力を必要とします。 次に示すのは一般的なガイドラインです。

  • エクステンションは自己充足的でなければなりません。つまり外部の依存は最小限でなければなりません。 エクステンションがさらなるパッケージ、クラスまたはリソースファイルのインストールを必要とするならば、 それはユーザの頭痛となるでしょう。
  • エクステンションに属しているファイルは、エクステンション名を名前とする同じディレクトリの下で組織される必要があります。
  • エクステンションのクラスは、他のエクステンションのクラスとの競合を避けるために、何文字かの識別子を名前の前に置かれなければなりません。
  • エクステンションには、コードと合せて詳細なインストールとAPIのドキュメンテーションを付けるべきです。 これにより、他の開発者がエクステンションを使う際に必要な時間と努力を減らします。
  • エクステンションは、適当なライセンスを持っていなければなりません。 オープンソースとクローズドソースプロジェクトの両方にエクステンションを使って貰いたければ BSD、MIT、その他のようなライセンスを考慮したほうが良いでしょう。GPLではありません。 なぜならGPLはその派生コードに同様にオープンソースであることを要求するためです。

以下に、概要で解説される分類により、新しいエクステンションを作成する方法を解説します。 主に自身のプロジェクトで使われるコンポーネントを作成するときも、これらの説明があてはまります。

1. アプリケーションコンポーネント

アプリケーションコンポーネント は[IApplicationComponent]インタフェースを実装するかまたはCApplicationComponentを継承しなければなりません。 実装する必要があるメインのメソッドは[IApplicationComponent::init]であり、そこでコンポーネントの何らかの初期化処理を行います。 このメソッドは、コンポーネントが生成され、アプリケーション構成 で規定されるプロパティの初期値が適用された後に呼び出されます。

デフォルトでは、アプリケーションコンポーネントは、リクエスト処理中に最初にアクセスされるときに生成され、初期化されます。 もしアプリケーションインスタンスが生成された直後にアプリケーションコンポーネントが生成される必要があるならば、 ユーザはそのIDをCApplication::preloadプロパティに記述しておかなければなりません。

2. ビヘイビア

ビヘイビアを作成するためには、[IBehavior]インタフェイスを実装しなければなりません。 便利なように、YiiはCBehaviorという基本クラスを提供して、その中で既にこのインタフェイスと いくつかの便利なメソッドを実装しています。これを継承する子クラスでは、主として、 アタッチされるコンポーネントに対して提供しようとする追加のメソッドを実装することが必要になります。

CModelおよびCActiveRecordのためのビヘイビアを開発する場合も、それぞれCModelBehavior およびCActiveRecordBehaviorを継承元にすることが可能です。これらの基本クラスは、 CModelCActiveRecordのために特に作られた追加の機能を提供してくれます。 例えば、CActiveRecordBehaviorクラスは、アクティブレコードオブジェクトのライフサイクル において生成されるイベントに応答する一連のメソッドを実装しています。従って、子クラスでは、 これらのメソッドをオーバーライドして、ARのライフサイクルに関与するカスタマイズされたコードを 格納することが出来ます。

下記のコードはアクティブレコードのビヘイビアの例を示しています。 このビヘイビアがARオブジェクトにアタッチされると、save()が呼ばれてARオブジェクトが 保存されるときに、自動的にcreate_timeupdate_timeの属性に現在のタイムスタンプが 設定されます。

class TimestampBehavior extends CActiveRecordBehavior
{
    public function beforeSave($event)
    {
        if($this->owner->isNewRecord)
            $this->owner->create_time=time();
        else
            $this->owner->update_time=time();
    }
}

3. ウィジェット

ウィジェットCWidgetまたはその子クラスを継承すべきものです。

新しいウィジェットを作成する最も簡単な方法は、既存のウィジェットを継承し、 そのメソッドをオーバライドするか、またはそのデフォルトプロパティ値を変更することです。 たとえば、より素晴らしいCSSスタイルをCTabViewに適用したい場合、 ウィジェットを使用する際にそのCTabView::cssFileプロパティを構成することもできます。 しかし、以下のようにCTabViewを継承すれば、ウィジェットを使う時にプロパティを構成しなくても 済むようにすることが可能になります。

class MyTabView extends CTabView
{
    public function init()
    {
        if($this->cssFile===null)
        {
            $file=dirname(__FILE__).DIRECTORY_SEPARATOR.'tabview.css';
            $this->cssFile=Yii::app()->getAssetManager()->publish($file);
        }
        parent::init();
    }
}

上記においては、CWidget:initメソッドをオーバライドして、プロパティがセットされてなければ 新しいデフォルトCSSスタイルのURLをCTabView:cssFileに割り当てています。 この新しいCSSスタイルファイルは、エクステンションとしてまとめられるように、 MyTabViewクラスファイルを含む同じディレクトリの下に置いています。 このCSSスタイルファイルはWebアクセスできないので、アセットとして公開する必要があります。

ゼロから新しいウィジェットを作製するためには、主にCWidget::initCWidget::runの2つのメソッドを実装する必要があります。 ウィジェットをビューに挿入するために$this->beginWidgetを使うときに、第1のメソッドが呼ばれ、$this->endWidgetを呼ぶときに、第2のメソッドが呼ばれます。 これらの2つのメソッドの間で表示される内容を捕えて処理したい場合には、CWidget::initの中で出力バッファリングを開始し、CWidget::runの中でバッファされた出力を取り出して追加の処理を行います。

ウィジェットは、しばしば、CSS、JavaScriptまたは他のリソースファイルをウィジェットを使うページに含むことが必要です。 これらのファイルはウィジェットクラスファイルと共にいて、通常はウェブユーザーからアクセスできないので、アセット(資産)と呼ばれます。 これらのファイルにウェブアクセスできるようにするため、上記のコード断片で示すように、[WebApplication::assetManager]を用いて公開する必要があります。 この他、CSSまたはJavaScriptファイルを現在のページに含めたいならば、CClientScriptを用いてそれを登録する必要があります。

class MyWidget extends CWidget
{
    protected function registerClientScript()
    {
        // ...CSS又はJavaScriptファイルをここで公開...
        $cs=Yii::app()->clientScript;
        $cs->registerCssFile($cssFile);
        $cs->registerScriptFile($jsFile);
    }
}

ウィジェットは、それ自身のビューファイルも持つことが出来ます。もしそうならば、ウィジェットクラスファイルを含んでいるディレクトリの下にviewsという名のディレクトリをつくり、すべてのビューファイルをそこに置いてください。 ウィジェットクラスでは、ウィジェットの表示を行うために$this->render('ViewName')を用いてください。 そしてそれはコントローラで行う方法と似ています。

4. アクション

アクションCActionまたはその子クラスから継承されるべきです。 アクションとして主に実装されるべきメソッドは[IAction::run]です。

5. フィルタ

フィルタCFilterまたはその子クラスから継承されるべきです。 フィルタとして主に実装されるべきメソッドはCFilter::preFilterCFilter::postFilterです。 前者はアクションが実行される前に呼び出され、後者はアクションの実行後に呼び出されます。

class MyFilter extends CFilter
{
    protected function preFilter($filterChain)
    {
        // アクションが実行される前に行われるロジック
        return true; // アクションが実行されるべきでない場合は偽
    }
 
    protected function postFilter($filterChain)
    {
        // アクションが実行された後に行われるロジック
    }
}

$filterChainパラメータはCFilterChainタイプで、現在フィルタされているアクションの情報を含みます。

6. コントローラ

エクステンションとして配布されるコントローラCControllerの継承ではなくCExtControllerを継承するべきです。 主な理由は、CControllerはビューファイルがapplication.views.ControllerIDの下に存在するのに対し、CExtControllerはビューファイルが、コントローラクラスファイルを含むディレクトリのサブディレクトリであるviewsディレクトリに存在すると仮定するからです。 したがって、そのビューファイルがコントローラクラスファイルと共に存在するため、コントローラを再配布することはより簡単です。

7. バリデータ

バリデータはCValidatorを継承し、CValidator::validateAttributeメソッドを実装するべきです。

class MyValidator extends CValidator
{
    protected function validateAttribute($model,$attribute)
    {
        $value=$model->$attribute;
        if($value has error)
            $model->addError($attribute,$errorMessage);
    }
}

8. コンソールコマンド

コンソールコマンドCConsoleCommandを継承し、 CConsoleCommand::runメソッドを実装するべきです。 オプションとして、CConsoleCommand::getHelpをオーバライドして、コマンドの素敵なヘルプ情報を表示することも可能です。

class MyCommand extends CConsoleCommand
{
    public function run($args)
    {
        // $argsは、このコマンドに対するコマンドラインの引数の配列です
    }
 
    public function getHelp()
    {
        return '使用法: このコマンドの使いかた';
    }
}

9. モジュール

モジュールの作成する方法についてはモジュールに関する章を参照してください。

モジュール開発の一般的なガイドラインは、モジュールは自己充足的であるべきだ、ということです。 モジュールによって使用されるリソースファイル(例えばCSS, JavaScript, 画像)はモジュールと共に配布されるべきです。 そしてモジュールはそれらのファイルをウェブアクセス可能なように公開しなければなりません。

10. 汎用コンポーネント

汎用コンポーネントのエクステンションの開発はクラスを書くことに似ています。 ここでも、コンポーネントは、他の開発者によって容易に使用されるように、自己充足的なものでなければなりません。

$Id: extension.create.txt 1423 2009-09-28 01:54:38Z qiang.xue $

Be the first person to leave a comment

Please to leave your comment.