※このページは現在更新をおこなっていません。 OpenPNE 3 については http://www.openpne.jp/http://readmine.openpne.jp/ などをご覧ください。

OpenPNE3プラグインの作り方

select languages

index

はじめに

OpenPNE3はあらゆる機能をプラグインとして追加することができます。 OpenPNE3はsymfonyフレームワークを利用していますが、OpenPNE3のプラグインはsymfonyのプラグインとは違ったプラグインの構造を持っています。 このドキュメントでは、OpenPNE3のプラグインの作成方法を紹介します。

前提条件

このドキュメントは、以下の条件が揃っていることを前提とします。

  • OpenPNE3がインストール・動作する環境がある
  • 上の環境においてコンソールでの作業が行える

プラグインの骨組みの作成

OpenPNE3は、プラグインの骨組みを作成するためのタスクも用意しています。 プラグインの作成は、そのタスクを利用して骨組みを作成します。

OpenPNE3のディレクトリに移動

コンソールでOpenPNE3がインストールされたディレクトリに移動します。

以下、「/var/www/OpenPNE3」にOpenPNE3をインストールした想定で説明します。

$ cd /var/www/OpenPNE3

タスクの実行

次に、プラグイン作成タスクを実行します。例として「opSamplePlugin」という名前のプラグインを作成する場合を挙げます。

以下のタスクを実行することにより、OpenPNE3の「plugins」ディレクトリ下に「opSamplePlugin」が追加されます。

$ symfony opGenerate:plugin opSamplePlugin

新しく追加された「opSamplePlugin」ディレクトリの中には、4つの空のディレクトリが作られます。

apps
conf
i18n
lib
補足:プラグインの命名規則について

現状ではプラグイン名の命名規則は未定ですが、

「opHogehogePlugin」

という方向で考えています。

モジュールの追加

symfonyプラグインとの大きな違いは、「apps」ディレクトリを配置できることにあります。 この下に、アプリケーションごとのモジュールを配置することができます。 また、これらの作成を自動的に行うためのタスクも用意されています。

例として、PCからのSNSメンバーが利用するpc_frontendアプリケーションに「hello」モジュールを追加する場合を挙げます。 以下のタスクで「apps」ディレクトリ下に空の「modules」ディレクトリが含まれた「pc_frontend」ディレクトリが作成されます。

$ symfony opGenerate:app opSamplePlugin pc_frontend

さらに、「pc_frontend」に「hello」モジュールを追加します。 以下のタスクで「pc_frontend/modules」ディレクトリ内に「hello」ディレクトリが作成されます。

$ symfony opGenerate:module opSamplePlugin pc_frontend hello

http://sns.example/

がOpenPNE3のURLであったとき、下記のURLにアクセスして「Module "hello" created」というページが表示されたら骨組みの完成です。

http://sns.example/hello

表示の変更

アクションの編集

「plugins/opSamplePlugin/app/pc_frontend/modules/hello」下には 「actions」と「templates」の2つのディレクトリが作成されます。 「actions」ディレクトリの中には、最初から「actions.class.php」が用意されています。 中身はこのようになっています。

…(省略)…
class helloActions extends sfActions
  {
  /**
  * Executes index action
  *
  * @param sfRequest $request A request object
  */
  public function executeIndex($request)
  {
    $this->forward(’default’, ‘module’);
  }
}

$this->forword('default','module');

となっているため、「Module “hello” created」のページにフォワードしています。ここを

return sfView::SUCCESS;

に置き換えてみます。

…(省略)…
class helloActions extends sfActions
  {
  /**
  * Executes index action
  *
  * @param sfRequest $request A request object
  */
  public function executeIndex($request)
  {
   return sfView::SUCCESS;
  }
}

テンプレートの編集

次に、テンプレートを編集します。「templates」ディレクトリには、最初から「indexSuccess.php」が用意されています。 executeIndex() で sfView::SUCCESS が返されるとこのテンプレートを表示します。中身は空白のファイルなので、適当な編集を行います。例として「Hello world!」と入力した場合を挙げます。

http://sns.example/hello

にアクセスすると、OpenPNEのロゴと「Hello world!」の文章が表示されます。

認証の有無の変更

この状態の「hello」モジュールは、ログインしていなくても表示させることができます。 ログイン認証を利用するために、「hello」ディレクトリの中に、新しく「config」ディレクトリを作成して「security.yml」というファイルを作成し、以下のような内容にします。

all:
  is_secure: on
  credentials: SNSMember

YAMLなので、インデントにはタブでなくて半角スペース2を使用します。 その後、 symfony cc でキャッシュを削除してください。

そうすると、ログインした状態でないと

http://sns.example/hello

にアクセスできなくなります。ログインしてアクセスすると、ログイン後のメニューが付いたページになります。

データベースモデルの作成

プラグイン独自のデータベースモデルを作成する方法です。 例として、MyNewsを作成する場合を挙げます。以降、MyNewsの例で進めていきます。

プラグイン用のデータベースモデルの作成法はsymfonyのマニュアルにある通りです。

<URL:http://www.symfony-project.org/book/1_2/17-Extending-Symfony#Plug-In%20File%20Structure>(英語)

プラグインディレクトリの直下にある「config」ディレクトリ内に「schema.yml」を作成します。 MyNewsを作る上で、必要なフィールドは以下の通りです。

ID
1対1のモデルにしてもよいのですが、今回はこのテーブルのための固有のキーを割り振ることにします。
メンバーID
コアにあるmemberモデルの外部キーです
本文
更新日時

このモデルを作成するために、「schema.yml」を次のように編集します。

propel:
  _attributes: { package: plugins.opSamplePlugin.lib.model }

  member_news:
    id: ~
    member_id : { type: integer, foreignTable: member, foreignReference: id }
    content:    { type: longvarchar }
    updated_at: ~

「security.yml」同様、YAML はタブ文字ではなくスペース2文字でインデントするということに気を付けてください。

id と指定したフィールドに関しては、特に何もオプションを指定しないと自動的にプライマリーキーとなり、オートインクリメントが有効になります。

●●●_id とやることにより、●●●テーブルの外部キーになります。同じ「schema.yml」ファイル上にそのテーブルがあれば、自動的にそのテーブルの外部キーになりますが、memberテーブルの宣言は別の場所にあるのでしっかりと foreignTableを指定します。

※ アルファ版の段階でコアのモデルを利用するときは、モデルの変更が行われることがありますのでご注意ください。

updated_atは更新時に自動的に更新時時刻が登録されるフィールドになります。 このファイルを保存した後、

$ symfony openpne:install

コマンドを利用して、データベースモデルを再構築します。

※ 将来的には、OpenPNE3のすべてのデータベースモデルを初期化しないで新しく作成したモデルを追加することができるようになる予定です。

パーツの追加

OpenPNE2のカスタマイズは、特定ページ(例えばpage_h_home)に新しい機能を加えるとき、 そのページのアクションを編集し、さらにテンプレートを編集し……、といった作業が必要でした。 OpenPNE3は、プラグインを追加するだけで特定ページに、新たな部品を追加することができます。

この仕組みをテンプレート拡張と呼んでいます。

OpenPNE3のテンプレートでは、複数のテンプレート部品(パーツ)によって構成される仕組みになっています。 パーツにはIDを持っていて、その前後に別のパーツを挿入することが可能です。

「My News!機能」を実装するためにはホームとプロフィールに新しく部品を挿入する必要があります。

informationの下に、新たに「My News!」のフォームを挿入します。まずはinformationの下に文字を表示できるようにしてみます。

パーシャルの追加

まずは、新たに挿入するテンプレートを追加します。

opSamplePlugin/apps/pc_frontend/modules/hello/templates

に新たに _sampleParts.phpを追加します。これは、informationの下に表示するパーシャルです。

symfonyのパーシャルについてはこちらをご覧ください。

<URL:http://www.symfony-project.org/book/1_2/07-Inside-the-View-Layer#Templating>(英語)

_samplePlarts.phpには「Hello world!!」と入力します。

レイアウト設定の作成

OpenPNE3のホームである、member/home に上に今回作成したテンプレートを挿入するため、

opSamplePlugin/apps/pc_frontend/modules

に、新たにmember ディレクトリ。さらにその中に config ディレクトリを作成します。 config ディレクトリに、 view.yml ファイルを作成して以下のように編集します。

homeSuccess:
  customize:
    sampleParts:
      template: [hello, sampleParts]
      parts: [information]
      target: [after] 
homeSuccess
memberモジュールで、homeSuccessテンプレート(homeアクションが成功したときに呼び出されるテンプレート)の設定をここで追加しています
customize
テンプレート拡張を実現します
sampleParts
テンプレート名です
template
特定のパーシャルを指定します
parts
パーツのidを指定します
target
partsで指定したidのどこに配置するかを定めます

これにより、homeSuccess の information の後に hello/sampleParts が挿入されるようになります。

設定ファイルを追加、編集した後には必ず、以下のコマンドでキャッシュを削除してください。

$ symfony cc

ホームにアクセスすると、informationの下に「Hello world!!」の文字が表示されるようになっています。

フォームの作成

フォームの編集

opSamplePlugin/lib/form にはあらかじめ、MemberNewsForm.class.phpが存在します。 これはモデルの構築時に自動的に作成されるクラスで、何も加工せずに利用するとすべてのフィールドが編集できるフォームを提供しています。 (id , member_id, content, update_at フィールドが編集できるフォーム)

MyNews!機能で編集可能な項目は content フィールドだけなので、そのように対応させるため、以下のように編集します。

MemberNewsForm.class.php
<?php

(コメント行省略)
class MemberNewsForm extends BaseMemberNewsForm
{
  public function configure()
  {
    $this->setWidgets(array(
      ’content’ => new sfWidgetFormTextarea()
    ));

    $this->setValidators(array(
      ’member_id’ => new sfValidatorPropelChoice(array(’model’ => ‘Member’, ‘column’ => ‘id’)),
      ’content’ => new sfValidatorString(array(’required’ => false))
    ));

    $this->widgetSchema->setNameFormat(’member_news[%s]‘);
  }
}

フォームの作成法については以下を参考にしてください。

The symfony Forms Book - 第1章 - フォームの作成
<URL:http://www.symfony-project.org/book/forms/1_2/ja/01-Form-Creation>
The symfony Forms Book - 第4章 - Propelとの統合(symfony1.1時点)
<URL:http://symfony.xrea.jp/1.1/forms_book/04-Propel-Integration.html>

モデルの操作

さらに、このフォームをパーシャルに挿入します。 opSamplePlugin/apps/pc_frontend/modules/hello/templates/_sampleParts.php を以下のように変更します。

_sampleParts.php
<?php
$criteria = new Criteria();
$criteria->add(MemberNewsPeer::MEMBER_ID,$sf_user->getMember()->getId());
$memberNews = MemberNewsPeer::doSelectOne($criteria);

$form = new MemberNewsForm($memberNews);

include_box(’MyNews’,'MyNews’,”,array(’form’=>$form,’url’=>’hello/updateNews’));

モデルの操作についてはこちらを参考にしてください。

The Definitive Guide to symfony - 第8章 - モデルレイヤーの内側(symfony1.1時点)
<URL:http://symfony.xrea.jp/1.1/book/08-Inside-the-Model-Layer.html>

このコードでは、先ほど編集したフォームのインスタンスを作成しています。 コンストラクタにモデルのインスタンスを渡すことにより、現在DBに登録されている内容がデフォルト値として設定されます。 フォームを出力するときには、OpenPNE3用パーツを挿入するための PartsHelper(最初からロードされています) に含まれている、include_box() を利用しています。

include_box($id, $title = ”, $body = ”, $option = array()) の説明
$id
このパーツのIDです。同一ページで同じIDを利用しないように注意してください。
$title
このパーツのタイトル部に表示させる文字列です。
$body
このパーツの内容です。MyNews!機能の場合はフォームを表示させるので空白です。
$option
パーツのオプションです
form
フォームを使用する場合に必要なパラメータです。formを渡すときはこのパラメータにフォームのインスタンスを渡します。
url
formがsubmitされたときのアクション先を指定します

編集後、更新するとOpenPNEのホームにMyNews!の編集フォームが表示されるようになっています。

アクションの編集

plugins/opSamplePlugin/apps/pc_frontend/modules/hello/actions/actions.class.php に、MyNewsが投稿されたときのアクションを記します。

「モデルの操作」で、ポスト先のアクション名は updateNews としたので、 このファイルで宣言している helloAction クラスに新たなメソッド「executeUpdateNews」を追加します。 メソッド名はexecuteアクション名()(アクション名の最初は大文字)という規則です。

actions.class.php
<?php
// (コメント行省略)..

class helloActions extends sfActions
{
  // (コメント行省略)..
  public function executeIndex($request)
  {
    return sfView::SUCCESS;
  }

  public function executeUpdateNews($request)
  {
    // POSTリクエストかどうかを確認する
    if ($request->isMethod(sfRequest::POST))
    {
      // member_news から 自分のメンバーIDが一致する1行を取りだす
      $criteria = new Criteria();
      $criteria->add(MemberNewsPeer::MEMBER_ID,$this->getUser()->getMember()->getId());
      $memberNews = MemberNewsPeer::doSelectOne($criteria);

      // 前回作成した memberNewsFormの初期値として先ほど取りだした1行をセットする
      $memberNewsForm = new MemberNewsForm($memberNews);

      // POSTされたパラメータを取得する
      $param = $request->getParameter(’member_news’);
      $param['member_id'] = $this->getUser()->getMember()->getId();

      // パラメータをバインドする
      $memberNewsForm->bind($param);

      // 入力された値が妥当かどうかを確かめる
      if ($memberNewsForm->isValid())
      {
        // データベースに保存
        $memberNewsForm->save();
      }
    }
    // 最後に自分のホームにリダイレクトする
    return $this->redirect(’@homepage’);
  }
}

アクションでは、$this->getUser()->getMemer()で自分のメンバー情報が取得できます。 これは自分の情報がセットされたクラス Member のインスタンスです。また、テンプレートでは $sf_user で $this->getUser() が取得できます。 よって、 $sf_user->getMember() で自分のメンバー情報が取得できます。

プロフィール画面での確認機能の作成

plugins/opSamplePlugin/apps/pc_frontend/modules/hello/templates

にパーシャル _sampleParts2.php を追加します。これでMyNewsを表示できるよう、以下のような編集をおこないます。

_sampleParts2.php
<?php
// idパラメータ(プロフィールページを表示するとき指定しているメンバーID)
// を取得します。自己プロフィールのように id がない場合は、自分のメンバーIDを $memberIdに代入する
$memberId = $sf_request->getParameter(’id’, $sf_user->getMember()->getId());

// member_news テーブルから取得した $memberIdと一致する1行を取り出す
$criteria = new Criteria();
$criteria->add(MemberNewsPeer::MEMBER_ID, $memberId);
$memberNews = MemberNewsPeer::doSelectOne($criteria);

$body = “”;
// もし行が存在した場合は、contentフィールドの内容を$bodyに代入する
if ($memberNews)
{
  $body = $memberNews->getContent();
}

// Partsヘルパー(OpenPNE3独自のヘルパー)に含まれる
// include_box関数を使って $body を出力する
// include_boxの仕様は前回の記事で説明
include_box(’MyNews’,'MyNews’,nl2br($body));

レイアウト設定の変更

templateに sampleParts2 を指定します。

plugins/opSamplePlugin/apps/pc_frontend/modules/member/config/view.ymlを以下のようにします。

view.yml
homeSuccess:
…(省略)…
      target: [after]

profileSuccess:
  customize:
    sampleParts2:
      template: [hello, sampleParts2]
      parts: [profile]
      target: [before]

設定ファイル編集後、 symfony cc でキャッシュ削除をします。 これでプロフィール画面にMyNewsの内容が表示されます。

これでMyNews!機能は完成です。