20日目: プラグイン
昨日は、symfony アプリケーションの国際化とローカライゼーションのしかたを学びました。再度繰り返しますが、ICU 標準とたくさんのヘルパーのおかげで、symfony ではこの作業は本当に楽です。
今日は、~プラグイン~は何ができ、プラグインに何をまとめることができるのか、何のために使うことができるのかについて説明します。
プラグイン
symfony のプラグイン
symfony プラグインはプロジェクトファイルのサブセットのパッケージを作成して配布する方法を提供します。プロジェクトのように、プラグインはクラス、ヘルパー、設定、タスク、モジュール、スキーマとアセットさえも格納できます。
~プライベートプラグイン~
プラグインの最初の使い方は複数のアプリケーションもしくは異なるプロジェクトの間でコードを楽に共有できるようにすることです。symfony アプリケーションはモデルのみを共有することを思い思い出してください。プラグインはさらに複数のアプリケーションの間でコンポーネントを共有する方法を提供します。
異なるプロジェクトもしくは同じモジュールの同じスキーマを~再利用する|コードの再利用~場合、これらをプラグインに移動させます。
プラグインは単なるディレクトリなので、SVN リポジトリを作り svn:externals
を指定するもしくは1つのプロジェクトから他のプロジェクトにファイルをコピーすることでプラグインを移動させることができます。
私たちはこれらを「プライベートプラグイン」と呼んでいます。これらの用途は1人の開発者もしくは企業に限定されているからです。 これらは公に利用できません。
TIP プライベートプラグインから~パッケージ|パッケージ作成~を作ることもできます。独自の symfony プラグインチャンネルを作成し、
plugin:install
タスクをとおしてこれらをインストールします。
~公開プラグイン~
公開プラグインは~コミュニティ~でダウンロードとインストールができます。
sfGuardPlugin
と sfFormExtraPlugin
を使いました。
sfDoctrineGuardPlugin
と sfFormExtraPlugin
を使いました。
これらはプライベートプラグインとまったく同じです。唯一の違いは誰でもプロジェクトにインストールできることです。後で公開プラグインを公開して symfony の公式サイトでホストする方法を見ることになります。
~コードを編成する|コードの編成~異なる方法
プラグインを考え出す方法とそれらの使い方はいろいろあります。再利用と共有を忘れましょう。プラグインはあなたのコードを編成するための異なる方法として利用できます。レイヤーごとにファイルを編成する代わりに: lib/model/
ディレクトリのすべてのモデル、templates/
ディレクトリのテンプレートなど; ファイルは機能ごとにまとめられます: すべての job ファイルのセット (モデル、モジュール、テンプレート)、すべての CMS ファイルのセットなど。
プラグインのファイル構造
プラグインはファイルの性質に応じて、あらかじめ定義された~構造~にわかれるファイルをもつ単なるディレクトリ構造です。今日は、Jobeetに向けて書いてきたコードの大部分を sfJobeetPlugin
に移動させます。使用する基本的なレイヤーは次のとおりです:
sfJobeetPlugin/
Jobeet プラグイン
プラグインのブートストラップはシンプルで plugins/
の下で新しいディレクトリを作ります。Jobeet に関して、sfJobeetPlugin
ディレクトリを作りましょう:
$ mkdir plugins/sfJobeetPlugin
それから、config/ProjectConfiguration.class.php
ファイルのなかで sfJobeetPlugin
を有効にします。
[php]
public function setup()
{
$this->enablePlugins(array(
'sfDoctrinePlugin',
'sfDoctrineGuardPlugin',
'sfFormExtraPlugin',
'sfJobeetPlugin'
));
}
NOTE すべてのプラグインの名前は
Plugin
で終わらなければなりません。義務ではありませんが、~プレフィックス~としてsf
をつけるのはよい習慣です。
Model
config/schema.yml
ファイルを plugins/sfJobeetPlugin/config/
に移動させます:
config/doctrine/schema.yml
ファイルを plugins/sfJobeetPlugin/config/
に移動させます:
$ mkdir plugins/sfJobeetPlugin/config/
NOTE すべてのコマンドは Unix 系環境のものです。Windows を利用しているのであれば、Explorer でファイルをドラッグ&ドロップできます。コードを管理するのに ~Subversion~、もしくはほかのツールを使う場合、これらが提供する組み込みのツールを使います (ファイルを移動させる
svn mv
など)。
モデル、フォーム、フィルタファイルを plugins/sfJobeetPlugin/lib/
に移動させます:
$ mkdir plugins/sfJobeetPlugin/lib/
$ mv lib/model/ plugins/sfJobeetPlugin/lib/
$ mv lib/form/ plugins/sfJobeetPlugin/lib/
$ mv lib/filter/ plugins/sfJobeetPlugin/lib/
Remove the plugins/sfJobeetPlugin/lib/form/BaseForm.class.php
file.
$ rm plugins/sfJobeetPlugin/lib/form/BaseForm.class.php
Plugin
をつけなければなりません。
TIP ~プレフィックス~の
Plugin
をつけるのは自動生成クラスのみですべてのクラスではありません。たとえば手書きのクラスにはプレフィックスをつけないでください。プレフィックスが必要なのは自動生成されたクラスのみです。
JobeetAffiliate
と JobeetAffiliateTable
クラスを移動させる例は次の通りです。
$ mv plugins/sfJobeetPlugin/lib/model/doctrine/JobeetAffiliate.class.php plugins/sfJobeetPlugin/lib/model/doctrine/PluginJobeetAffiliate.class.php
コードは次のように更新されます:
[php]
abstract class PluginJobeetAffiliate extends BaseJobeetAffiliate
{
public function save(Doctrine_Connection $conn = null)
{
if (!$this->getToken())
{
$this->setToken(sha1($object->getEmail().rand(11111, 99999)));
}
parent::save($conn);
}
// ...
}
JobeetAffiliateTable
クラスを移動させましょう:
$ mv plugins/sfJobeetPlugin/lib/model/doctrine/JobeetAffiliateTable.class.php plugins/sfJobeetPlugin/lib/model/doctrine/PluginJobeetAffiliateTable.class.php
クラスの定義は次のようになります:
[php]
abstract class PluginJobeetAffiliateTable extends Doctrine_Table
{
// ...
}
フォームとフィルタクラスにも同じことを行います。Plugin
をプレフィックスとしてつけるためにこれらをリネームします。
form
、filter
と model
ディレクトリに対して plugins/sfJobeetPlugin/lib/*/doctrine/
の base
ディレクトリを必ず削除してください:
$ rm -rf plugins/sfJobeetPlugin/lib/form/doctrine/base
$ rm -rf plugins/sfJobeetPlugin/lib/filter/doctrine/base
$ rm -rf plugins/sfJobeetPlugin/lib/model/doctrine/base
フォーム、フィルタとモデルクラスを移動させ、リネームして一部を削除したのですべてのクラスをリビルドするタスクを実行します:
$ php symfony doctrine:build --all-classes
いくつかの新しく作成されたディレクトリが lib/model/doctrine/sfJobeetPlugin/
に格納されるスキーマから作成されたモデルを保有こしていることがわかります。
このディレクトリはスキーマから生成されたトップレベルのモデルと基底クラスを含みます。たとえば JobeetJob
モデルは次のようなクラス構造を持ちます:
JobeetJob
(PluginJobeetJob
を継承)lib/model/doctrine/sfJobeetPlugin/JobeetJob.class.php
: すべてのプロジェクトの機能が設定できるトップレベルのクラスです。ここは機能を追加したりプラグインモデルに付属する機能をオーバーライドできる場所です。PluginJobeetJob
(BaseJobeetJob
を継承)plugins/sfJobeetPlugin/lib/model/doctrine/PluginJobeetJob.class.php
: このクラスはプラグイン固有の機能すべてを格納します。JobeetJob
クラスを修正することでこのクラスと基底クラスをオーバーライドできます。BaseJobeetJob
(sfDoctrineRecord
を継承)lib/model/doctrine/sfJobeetPlugin/base/BaseJobeetJob.class.php
:doctrine:build --model
を実行するたびにYAMLスキーマファイルから自動生成される基底クラス。JobeetJobTable
(PluginJobeetJobTable
を継承)lib/model/doctrine/sfJobeetPlugin/JobeetJobTable.class.php
:Doctrine_Core::getTable('JobeetJob')
を呼び出す際に返されるDoctrine_Table
のインスタンスであることを除いてJobeetJob
クラスと同じPluginJobeetJobTable
(Doctrine_Table
を継承)lib/model/doctrine/sfJobeetPlugin/JobeetJobTable.class.php
: このクラスはDoctrine_Core::getTable('JobeetJob')
を呼び出す際に返されるDoctrine_Table
のインスタンス用のプラグイン固有の機能をすべて含みます
この生成された構造によってトップレベルの JobeetJob
クラスを編集することでプラグインのモデルをカスタマイズできます。
setTableDefinition()
と setUp()
メソッドをオーバーライドすることでスキーマとカラムをカスタマイズし、リレーションシップを追加できます。
NOTE フォームクラスを移動させるとき、かならず
configure()
メソッドをsetup()
メソッドに変更しparent::setup()
を呼び出してください。下記のコードは例です。[php] abstract class PluginJobeetAffiliateForm extends BaseJobeetAffiliateForm { public function setup() { parent::setup(); } // ... }
propel:build --model
タスクを実行する場合、symfony は lib/model/
の下でファイルを生成しますが、これは私たちが望むことではありません。Propel の出力ディレクトリは package
オプションを追加することで設定できます。schema.yml
を開き次のコンフィギュレーションを追加します:
[yml]
# plugins/sfJobeetPlugin/config/schema.yml
propel:
_attributes: { package: plugins.sfJobeetPlugin.lib.model }
これで symfony は plugins/sfJobeetPlugin/lib/model/
ディレクトリの下でファイルを生成します。フォームとフィルタビルダーはファイルを生成する際にこの設定も考慮します。
propel:build --sql
タスクはテーブルを作成する SQL ファイルを生成します。ファイルはパッケージの名前からつけられるので、現在のファイルを削除します:
$ rm data/sql/lib.model.schema.sql
propel:build --all --and-load
を実行する場合、symfony はプラグインの lib/model/
ディレクトリの下でファイルを生成します:
$ php symfony propel:build --all --and-load --no-confirmation
タスクを実行した後で、lib/model/
ディレクトリが作成されなかったことを確認します。タスクは lib/form/
と lib/filter/
ディレクトリを作成しました。これらは両方ともプロジェクトの Propel フォーム用の基底クラスを格納します。
これらのファイルはプロジェクトに対してグローバルなので、プラグインからこれらのファイルを削除します:
$ rm plugins/sfJobeetPlugin/lib/form/BaseFormPropel.class.php
$ rm plugins/sfJobeetPlugin/lib/filter/BaseFormFilterPropel.class.php
doctrine:build --forms
と doctrine:build --filters
で再生成されます。
プラグインからファイルを削除します:
$ rm plugins/sfJobeetPlugin/lib/form/doctrine/BaseFormDoctrine.class.php
$ rm plugins/sfJobeetPlugin/lib/filter/doctrine/BaseFormFilterDoctrine.class.php
Jobeet.class.php
ファイルをプラグインに移動させることもできます:
$ mv lib/Jobeet.class.php plugins/sfJobeetPlugin/lib/
ファイルを移動させたので、キャッシュをクリアします:
$ php symfony cc
TIP APC のような PHP アクセラレータを利用している場合はこの時点で動作がおかしくなるので、Apache を再起動させます。
すべてのモデルファイルはプラグインに移動させたので、すべてがきちんと動作するのか確認するためにテストを実行します:
$ php symfony test:all
Controller と View
次のロジックのステップはモジュールをプラグインに移動させることです。モジュールの名前の衝突を避けるために、モジュールの名前に~プレフィックス~としてプラグインの名前をつけるのは常によい習慣です:
$ mkdir plugins/sfJobeetPlugin/modules/
$ mv apps/frontend/modules/affiliate plugins/sfJobeetPlugin/modules/sfJobeetAffiliate
$ mv apps/frontend/modules/api plugins/sfJobeetPlugin/modules/sfJobeetApi
$ mv apps/frontend/modules/category plugins/sfJobeetPlugin/modules/sfJobeetCategory
$ mv apps/frontend/modules/job plugins/sfJobeetPlugin/modules/sfJobeetJob
$ mv apps/frontend/modules/language plugins/sfJobeetPlugin/modules/sfJobeetLanguage
それぞれのモジュールに対して、すべての actions.class.php
と components.class.php
ファイルのクラスの名前を変更することも必要です (たとえば、affiliateActions
クラスは sfJobeetAffiliateActions
にリネームする必要があります)。
次のテンプレートの include_partial()
と include_component()
呼び出しも変更しなければなりません:
sfJobeetAffiliate/templates/_form.php
(affiliate
をsfJobeetAffiliate
に変更する)sfJobeetCategory/templates/showSuccess.atom.php
sfJobeetCategory/templates/showSuccess.php
sfJobeetJob/templates/indexSuccess.atom.php
sfJobeetJob/templates/indexSuccess.php
sfJobeetJob/templates/searchSuccess.php
sfJobeetJob/templates/showSuccess.php
apps/frontend/templates/layout.php
search
と delete
アクションを更新します:
[php]
// plugins/sfJobeetPlugin/modules/sfJobeetJob/actions/actions.class.php
class sfJobeetJobActions extends sfActions
{
public function executeSearch(sfWebRequest $request)
{
$this->forwardUnless($query = $request->getParameter('query'), 'sfJobeetJob', 'index');
if ($request->isXmlHttpRequest())
{
if ('*' == $query || !$this->jobs)
{
return $this->renderText('No results.');
}
return $this->renderPartial('sfJobeetJob/list',
➥ array('jobs' => $this->jobs));
}
}
public function executeDelete(sfWebRequest $request)
{
$request->checkCSRFProtection();
$jobeet_job = $this->getRoute()->getObject();
$jobeet_job->delete();
$this->redirect('sfJobeetJob/index');
}
// ...
}
最後に、これらの修正が考慮されるように routing.yml
ファイルを修正します:
[yml]
# apps/frontend/config/routing.yml
affiliate:
class: sfPropelRouteCollection
options:
model: JobeetAffiliate
actions: [new, create]
object_actions: { wait: GET }
prefix_path: /:sf_culture/affiliate
module: sfJobeetAffiliate
requirements:
sf_culture: (?:fr|en)
api_jobs:
url: /api/:token/jobs.:sf_format
class: sfPropelRoute
param: { module: sfJobeetApi, action: list }
options: { model: JobeetJob, type: list, method: getForToken }
requirements:
sf_format: (?:xml|json|yaml)
category:
url: /:sf_culture/category/:slug.:sf_format
class: sfPropelRoute
param: { module: sfJobeetCategory, action: show, sf_format: html }
options: { model: JobeetCategory, type: object, method: doSelectForSlug }
requirements:
sf_format: (?:html|atom)
sf_culture: (?:fr|en)
job_search:
url: /:sf_culture/search
param: { module: sfJobeetJob, action: search }
requirements:
sf_culture: (?:fr|en)
job:
class: sfPropelRouteCollection
options:
model: JobeetJob
column: token
object_actions: { publish: PUT, extend: PUT }
prefix_path: /:sf_culture/job
module: sfJobeetJob
requirements:
token: \w+
sf_culture: (?:fr|en)
job_show_user:
url: /:sf_culture/job/:company_slug/:location_slug/:id/:position_slug
class: sfPropelRoute
change_language:
url: /change_language
param: { module: sfJobeetLanguage, action: changeLanguage }
localized_homepage:
url: /:sf_culture/
param: { module: sfJobeetJob, action: index }
requirements:
sf_culture: (?:fr|en)
homepage:
url: /
param: { module: sfJobeetJob, action: index }
Jobeet の Web サイトを見ようとすると、モジュールが有効にされていないことを知らせる例外が表示されます。プラグインはプロジェクトのすべてのアプリケーションの間で共有されるので、アプリケーションに必要なモジュールを settings.yml
設定ファイルで有効にする必要があります:
[yml]
# apps/frontend/config/settings.yml
all:
.settings:
enabled_modules:
- default
- sfJobeetAffiliate
- sfJobeetApi
- sfJobeetCategory
- sfJobeetJob
- sfJobeetLanguage
マイグレーションの最後のステップはモジュールの名前に対してテストする機能テストを修正することです。
タスク
タスクはプラグインに簡単に移動させることができます:
$ mv lib/task plugins/sfJobeetPlugin/lib/
国際化ファイル
プラグインは XLIFF ファイルも格納できます:
$ mv apps/frontend/i18n plugins/sfJobeetPlugin/
ルーティング
プラグインはルーティングルールも格納できます:
$ mv apps/frontend/config/routing.yml plugins/sfJobeetPlugin/config/
アセット
少し直感に反していますが、プラグインは、画像、スタイルシートとJavaScriptのようなWebアセットも格納できます。Jobeet プラグインを配布したくないので、本当に意味がありませんが、plugins/sfJobeetPlugin/web/
ディレクトリを作ることで実現できます。
プラグインのアセットはブラウザから閲覧可能なプロジェクトの web/
ディレクトリからアクセスできなければなりません。plugin:publish-assets
は Unix 系システムではシンボリックリンクの作成、Windows プラットフォームではファイルをコピーすることでこれに対処します:
$ php symfony plugin:publish-assets
ユーザー
求人履歴を扱う myUser
クラスメソッドを移動させることは少し手間がかかります。JobeetUser
クラスを作成し myUser
を継承させることもできます。しかし、とりわけいくつかのプラグインが新しいメソッドをクラスに追加したい場合、ベターな方法があります。
リスニングできるライフサイクルのあいだに symfony のコアオブジェクトはイベントを通知します。私たちの事例では、未定義のメソッドが sfUser
オブジェクトに呼び出されるときに起きる user.method_not_found
イベントをリスニングする必要があります。
symfony が初期化されるときにプラグインのコンフィギュレーションクラスがある場合、すべてのプラグインも初期化されます:
[php]
// plugins/sfJobeetPlugin/config/sfJobeetPluginConfiguration.class.php
class sfJobeetPluginConfiguration extends sfPluginConfiguration
{
public function initialize()
{
$this->dispatcher->connect('user.method_not_found', array('JobeetUser', 'methodNotFound'));
}
}
イベントの通知はイベントディスパッチャーオブジェクトである sfEventDispatcher
によって管理されます。connect()
メソッドを呼び出せばリスナーが登録されます。connect()
メソッドはイベントの名前を PHP callable に結びつけます。
NOTE PHP callable は PHP 変数で
call_user_func()
関数によって使われis_callable()
関数に渡されるときにtrue
を返します。文字列は関数を表し、配列はオブジェクトメソッドもしくはクラスメソッドを表します。
上記のコードによって、myUser
オブジェクトはメソッドを見つけられないときは JobeetUser
クラスの methodNotFound()
スタティックメソッドを呼び出します。見つからないメソッドを処理するもしくは処理しないのは methodNotFound()
メソッドしだいです。
myUser
クラスからすべてのメソッドを削除し JobeetUser
クラスを作ります:
[php]
// apps/frontend/lib/myUser.class.php
class myUser extends sfBasicSecurityUser
{
}
// plugins/sfJobeetPlugin/lib/JobeetUser.class.php
class JobeetUser
{
static public function methodNotFound(sfEvent $event)
{
if (method_exists('JobeetUser', $event['method']))
{
$event->setReturnValue(call_user_func_array(
array('JobeetUser', $event['method']),
array_merge(array($event->getSubject()), $event['arguments'])
));
return true;
}
}
static public function isFirstRequest(sfUser $user, $boolean = null)
{
if (is_null($boolean))
{
return $user->getAttribute('first_request', true);
}
else
{
$user->setAttribute('first_request', $boolean);
}
}
static public function addJobToHistory(sfUser $user, JobeetJob $job)
{
$ids = $user->getAttribute('job_history', array());
if (!in_array($job->getId(), $ids))
{
array_unshift($ids, $job->getId());
$user->setAttribute('job_history', array_slice($ids, 0, 3));
}
}
static public function getJobHistory(sfUser $user)
{
if (!empty($ids))
{
return Doctrine_Core::getTable('JobeetJob')
->createQuery('a')
->whereIn('a.id', $ids)
->execute();
}
return array();
}
static public function resetJobHistory(sfUser $user)
{
$user->getAttributeHolder()->remove('job_history');
}
}
ディスパッチャーが methodNotFound()
メソッドを呼び出すとき、sfEvent
オブジェクトを渡します。
JobeetUser
クラスにメソッドが存在する場合、このメソッドが呼び出され、その後で戻り値は通知元オブジェクトに返されます。そうではない場合、symfony は次に登録されたリスナーを試すもしくは例外を投げます。
getSubject()
メソッドはイベントの通知元オブジェクトを返します。この場合現在の myUser
オブジェクトです。
デフォルト構造 vs プラグインアーキテクチャ
プラグインのアーキテクチャを利用することで異なる方法でコードを編成できます:
プラグインを使う
新しい機能の実装を始めるとき、もしくは Web の古典的な問題を解決しようとする場合、おそらく誰かが同じ問題をすでに解決していて symfony のプラグインとして解決方法をパッケージにしています。symfony の公開プラグインを探すには、symfony 公式サイトのプラグインセクションに移動します。
プラグインはディレクトリに内蔵されるので、インストールする方法は複数あります:
plugin:install
タスクを使う (プラグインの開発者がプラグインパッケージを作り、symfony の公式サイトにアップロードする場合のみ機能する)- パッケージをダウンロードして
plugins/
ディレクトリの下で手動で解凍する (開発者がパッケージをアップロードすることも必要) plugins/
でプラグイン用のsvn:externals
を作る (プラグインの作者が Suversion でプラグインをホストする場合のみ機能する)
後者の2つの方法は簡単ですが柔軟性に欠けます。最初の方法でプロジェクトの symfony のバージョンに合わせて最新バージョンをインストールして、最新の安定版に簡単にアップグレードする、そして簡単にプラグイン間の依存関係を管理できます。
プラグインを寄付する
プラグインのパッケージを作成する
プラグインパッケージを作成するには、必須のファイルをプラグインのディレクトリ構造に追加する必要があります。最初に、プラグインのルートディレクトリで README
ファイルを作りプラグインのインストール方法、これが何を提供するもの、しないものを記述します。README
ファイルは Markdown フォーマットでフォーマットしなければなりません。このファイルは symfony 公式サイトでドキュメントのメインピースとして使われます。symfony plugin dingus を利用して README ファイルを HTML に変換することができます。
SIDEBAR プラグイン開発のタスク
プライベートかつ/もしくは公開プラグインをよく作ることに気がついたら、sfTaskExtraPlugin のタスクのいくつかを利用することを考えてください。このプラグインは、コアチームによって維持され、プラグインのライフサイクルの効率化を手助けするたくさんのタスクを含みます:
generate:plugin
plugin:package
LICENSE
ファイルを作ることも必要です。
ライセンスの選択は簡単な作業ではありませんが、symfony のプラグインセクションは symfony のライセンス (MIT、BSD、LGPL と PHP) と似たライセンスでリリースされたプラグインのみを表示します。LICENSE
ファイルの内容はプラグインの公開ページの license タブの下で表示されます。
最後のステップはプラグインディレクトリのルートで package.xml
ファイルを作ることです。この package.xml
ファイルは PEAR パッケージ構文に従います。
NOTE
package.xml
の構文を学ぶベストな方法は既存のプラグインで使われるファイルをコピーすることです。
package.xml
ファイルはいくつかの部分で構成されます。このテンプレートの例は次のようなものです:
[xml]
<!-- plugins/sfJobeetPlugin/package.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<package packagerversion="1.4.1" version="2.0"
xmlns="http://pear.php.net/dtd/package-2.0"
xmlns:tasks="http://pear.php.net/dtd/tasks-1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>sfJobeetPlugin</name>
<channel>plugins.symfony-project.org</channel>
<summary>A job board plugin.</summary>
<description>A job board plugin.</description>
<lead>
<name>Fabien POTENCIER</name>
<user>fabpot</user>
<email>fabien.potencier@symfony-project.com</email>
<active>yes</active>
</lead>
<date>2008-12-20</date>
<version>
<release>1.0.0</release>
<api>1.0.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://www.symfony-project.com/license">
MIT license
</license>
<notes />
<contents>
<!-- CONTENT -->
</contents>
<dependencies>
<!-- DEPENDENCIES -->
</dependencies>
<phprelease>
</phprelease>
<changelog>
<!-- CHANGELOG -->
</changelog>
</package>
<contents>
タグはパッケージに設置する必要のあるファイルを含みます:
[xml]
<contents>
<dir name="/">
<file role="data" name="README" />
<file role="data" name="LICENSE" />
<dir name="config">
<file role="data" name="config.php" />
<file role="data" name="schema.yml" />
</dir>
<!-- ... -->
</dir>
</contents>
<dependencies>
タグはプラグインがもつすべての依存関係の参照: PHP、symfony とほかのプラグインをつけます。この情報はプロジェクト環境に対してベストなプラグインのバージョンをインストールし存在するのであれば必要なプラグインの依存関係をインストールするために plugin:install
タスクによって使われます。
[xml]
<dependencies>
<required>
<php>
<min>5.0.0</min>
</php>
<pearinstaller>
<min>1.4.1</min>
</pearinstaller>
<package>
<name>symfony</name>
<channel>pear.symfony-project.com</channel>
<min>1.3.0</min>
<max>1.5.0</max>
<exclude>1.5.0</exclude>
</package>
</required>
</dependencies>
symfony はバージョンによって微妙に異なる API をもつので、ここで行ったように、依存関係を常に宣言すべきです。最大バージョンと最小バージョンを宣言すれば plugin:install
は symfony の必須バージョンがわかります。
別のプラグインとの依存関係を宣言することも可能です:
[xml]
<package>
<name>sfFooPlugin</name>
<channel>plugins.symfony-project.org</channel>
<min>1.0.0</min>
<max>1.2.0</max>
<exclude>1.2.0</exclude>
</package>
<changelog>
タグはオプションですがリリースの間の変更に関する有益な情報が得られます。この情報は "Changelog" タブとプラグインのフィードで見ることができます。
[xml]
<changelog>
<release>
<version>
<release>1.0.0</release>
<api>1.0.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://www.symfony-project.com/license">
MIT license
</license>
<date>2008-12-20</date>
<license>MIT</license>
<notes>
* fabien: First release of the plugin
</notes>
</release>
</changelog>
symfony 公式サイトでプラグインを公開する
便利なプラグインを開発して symfony のコミュニティと共有したい場合、まだなければ symfony 公式サイトのアカウントを作成し、新しいプラグインを作成します。
あなたは自動的にプラグインの管理者になりインターフェイスの "admin" タブを見ることになります。このタブにおいて、プラグインを管理してパッケージをアップロードするために必要なすべての機能が見つかります。
NOTE プラグインの FAQ ページにはプラグイン開発者のための有益な情報がたくさんあります。
また明日
プラグインの作成とコミュニティとの共有は symfony 公式サイトへのベストな貢献方法の1つです。これはとても簡単なので、symfony のプラグインリポジトリは便利で面白く、しかしおかしなプラグインで満たされています。
ORM