エクステンションの作成 =================== エクステンションは第三者の開発者によって使われるはずのものなので、作るためにはさらにいくらかの努力を必要とします。 次に示すのは一般的なガイドラインです。 * エクステンションは自己充足的でなければなりません。つまり外部の依存は最小限でなければなりません。 エクステンションがさらなるパッケージ、クラスまたはリソースファイルのインストールを必要とするならば、それはユーザの頭痛となるでしょう。 * エクステンションに属しているファイルは、エクステンション名を名前とする同じディレクトリの下で組織される必要があります。 * エクステンションのクラスは、他のエクステンションのクラスとの競合を避けるために、何文字かの識別子を名前の前に置かれなければなりません。 * エクステンションには、コードと合せて詳細なインストールと API のドキュメンテーションを付けるべきです。 これにより、他の開発者がエクステンションを使う際に必要な時間と努力を減らします。 * エクステンションは、適当なライセンスを持っていなければなりません。 オープンソースとクローズドソースプロジェクトの両方にエクステンションを使って貰いたければ BSD, MIT, その他のようなライセンスを考慮したほうが良いでしょう。 GPL ではありません。 なぜなら GPL はその派生コードに同様にオープンソースであることを要求するためです。 以下に、[概要](/doc/guide/extension.overview) で解説される分類により、新しいエクステンションを作成する方法を解説します。 主として自分自身のプロジェクトで使うコンポーネントを作成するときも、これらの説明があてはまります。 アプリケーションコンポーネント --------------------- [アプリケーションコンポーネント](/doc/guide/basics.application#sec-4) は [IApplicationComponent] インタフェースを実装するかまたは [CApplicationComponent] を継承しなければなりません。 実装する必要があるメインのメソッドは [IApplicationComponent::init] であり、そこでコンポーネントの何らかの初期化処理を行います。 このメソッドは、コンポーネントが生成され、[アプリケーション初期構成](/doc/guide/basics.application#sec-2) で規定されるプロパティの初期値が適用された後に呼び出されます。 デフォルトでは、アプリケーションコンポーネントは、リクエスト処理中に最初にアクセスされるときに生成され、初期化されます。 もしアプリケーションインスタンスが生成された直後にアプリケーションコンポーネントが生成される必要があるならば、ユーザはその ID を [CApplication::preload] プロパティに記述しておかなければなりません。 ビヘイビア -------- ビヘイビアを作成するためには、[IBehavior] インタフェースを実装しなければなりません。 便利なように、Yii は [CBehavior] という基本クラスを提供して、その中で既にこのインタフェースといくつかの便利なメソッドを実装しています。 これを継承する子クラスでは、主として、アタッチされるコンポーネントに対して提供しようとする追加のメソッドを実装することが必要になります。 [CModel] および [CActiveRecord] のためのビヘイビアを開発する場合も、それぞれ [CModelBehavior] および [CActiveRecordBehavior] を継承元にすることが可能です。 これらの基本クラスは、[CModel] や [CActiveRecord] のために特に作られた追加の機能を提供してくれます。 例えば、[CActiveRecordBehavior] クラスは、アクティブレコードオブジェクトのライフサイクルにおいて生成されるイベントに応答する一連のメソッドを実装しています。 従って、子クラスでは、これらのメソッドをオーバーライドして、AR のライフサイクルに関与するカスタマイズされたコードを格納することが出来ます。 下記のコードはアクティブレコードのビヘイビアの例を示しています。 このビヘイビアが AR オブジェクトにアタッチされると、`save()` が呼ばれて AR オブジェクトが保存されるときに、自動的に `create_time` と `update_time` の属性に現在のタイムスタンプが設定されます。 ~~~ [php] class TimestampBehavior extends CActiveRecordBehavior { public function beforeSave($event) { if($this->owner->isNewRecord) $this->owner->create_time=time(); else $this->owner->update_time=time(); } } ~~~ ウィジェット ------ [ウィジェット](/doc/guide/basics.view#sec-3) は [CWidget] またはその子クラスを継承すべきものです。 新しいウィジェットを作成する最も簡単な方法は、既存のウィジェットを継承し、そのメソッドをオーバライドするか、またはそのデフォルトプロパティ値を変更することです。 たとえば、より素晴らしい CSS スタイルを [CTabView] に適用したい場合、ウィジェットを使用する際にその [CTabView::cssFile] プロパティを構成することもできます。 しかし、以下のように [CTabView] を継承すれば、ウィジェットを使う時にプロパティを構成しなくても済むようになります。 ~~~ [php] 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 スタイルファイルはウェブアクセスできないので、アセットとして公開する必要があります。 ゼロから新しいウィジェットを作製するためには、主に [CWidget::init] と [CWidget::run] の二つのメソッドを実装する必要があります。 ウィジェットをビューに挿入するために `$this->beginWidget` を使うときに、第一のメソッドが呼ばれ、`$this->endWidget` を呼ぶときに、第二のメソッドが呼ばれます。 これらの二つのメソッドの間で表示される内容を捕えて処理したい場合には、[CWidget::init] の中で [出力バッファリング](http://us3.php.net/manual/en/book.outcontrol.php) を開始し、[CWidget::run] の中でバッファされた出力を取り出して追加の処理を行います。 ウィジェットは、しばしば、CSS, JavaScript または他のリソースファイルをウィジェットを使うページに含むことが必要です。 これらのファイルはウィジェットクラスファイルと共にいて、通常はウェブユーザからアクセスできないので、**アセット (資産)** と呼ばれます。 これらのファイルにウェブアクセスできるようにするため、上記のコード断片で示すように、[CWebApplication::assetManager] を用いて公開する必要があります。 この他、CSS または JavaScript ファイルを現在のページに含めたいならば、[CClientScript] を用いてそれを登録する必要があります。 ~~~ [php] class MyWidget extends CWidget { protected function registerClientScript() { // ...CSS または JavaScript ファイルをここで公開... $cs=Yii::app()->clientScript; $cs->registerCssFile($cssFile); $cs->registerScriptFile($jsFile); } } ~~~ ウィジェットは、それ自身のビューファイルも持つことが出来ます。 もしそうならば、ウィジェットクラスファイルを含んでいるディレクトリの下に `views` という名のディレクトリをつくり、すべてのビューファイルをそこに置いてください。 ウィジェットクラスでは、ウィジェットの表示を行うために `$this->render('ViewName')` を用いてください。 これはコントローラでビューを表示するのと同じ方法です。 アクション ------ [アクション](/doc/guide/basics.controller#sec-4) は [CAction] またはその子クラスから継承されるべきです。 アクションとして主に実装されるべきメソッドは [IAction::run] です。 フィルタ ------ [フィルタ](/doc/guide/basics.controller#sec-5) は [CFilter] またはその子クラスから継承されるべきです。 フィルタとして主に実装されるべきメソッドは [CFilter::preFilter] と [CFilter::postFilter] です。 前者はアクションが実行される前に呼び出され、後者はアクションの実行後に呼び出されます。 ~~~ [php] class MyFilter extends CFilter { protected function preFilter($filterChain) { // アクションが実行される前に行われるロジック return true; // アクションが実行されるべきでない場合は偽 } protected function postFilter($filterChain) { // アクションが実行された後に行われるロジック } } ~~~ `$filterChain` パラメータは [CFilterChain] タイプで、現在フィルタされているアクションの情報を含みます。 コントローラ ---------- エクステンションとして配布される [コントローラ](/doc/guide/basics.controller) は [CController] の継承ではなく [CExtController] を継承するべきです。 主な理由は、[CController] はビューファイルが `application.views.ControllerID` の下に存在するのに対し、[CExtController] はビューファイルが、コントローラクラスファイルを含むディレクトリのサブディレクトリである `views` ディレクトリに存在すると仮定するからです。 その結果、ビューファイルがコントローラクラスファイルと共に存在するため、コントローラを再配布するが一層容易になます。 バリデータ --------- バリデータは [CValidator] を継承し、[CValidator::validateAttribute] メソッドを実装するべきです。 ~~~ [php] class MyValidator extends CValidator { protected function validateAttribute($model,$attribute) { $value=$model->$attribute; if($value has error) $model->addError($attribute,$errorMessage); } } ~~~ コンソールコマンド --------------- [コンソールコマンド](/doc/guide/topics.console) は [CConsoleCommand] を継承し、[CConsoleCommand::run] メソッドを実装するべきです。 オプションとして、[CConsoleCommand::getHelp] をオーバライドして、コマンドの素敵なヘルプ情報を表示することも可能です。 ~~~ [php] class MyCommand extends CConsoleCommand { public function run($args) { // $argsは、このコマンドに対するコマンドラインの引数の配列です } public function getHelp() { return '使用法: このコマンドの使いかた'; } } ~~~ モジュール ------ モジュールを作成する方法については [モジュール](/doc/guide/basics.module#sec-2) に関する章を参照してください。 モジュール開発の一般的なガイドラインは、モジュールは自己充足的であるべきだ、ということです。 モジュールによって使用されるリソースファイル (例えばCSS, JavaScript, 画像) はモジュールと共に配布されるべきです。 そしてモジュールはそれらのファイルをウェブアクセス可能なように公開しなければなりません。 汎用コンポーネント ----------------- 汎用コンポーネントのエクステンションの開発はクラスを書くことに似ています。 ここでも、コンポーネントは、他の開発者によって容易に使用されるように、自己充足的なものでなければなりません。