baserCMSのブログ機能にカスタム項目を追加するプラグインを作成する方法

今回は、WordPress のカスタムフィールドのように、ブログ機能に、カスタム項目を追加するプラグインの作成方法をご紹介します。

プラグインでカスタム項目を追加するには、まず、baserCMS の「BaserEvent」という仕組みを知る必要があります。BaserEvent とは、baserCMS の土台となるフレームワークである、「CakeEvent」の baserCMS による拡張機能となり、WordPress のアクションフックや、フィルターフックのように、本体の処理に、別の処理を割り込ませる仕組みです。

仕様策定

それでは、まず、ブログ機能にカスタム項目を追加する為に、SampleField というプラグインを作成する前提で、インプット、ストア、アウトプットの3つの側面から仕様を整理しましょう。

インプット

インプットにおいては、ブログ記事の新規追加・編集画面に「sample」 というテキストエリアを追加し、入力できる仕様とします。
テキストエリアの配置箇所は、公開状態欄の下とします。

スクリーンショット 2015-05-03 18.16.11.png

 

ストア

追加した、sample というフィールドの保存先は、SampleField プラグイン用のテーブル 「sample_field」を作成し、そこに保存する仕様とします。
sample_field は、ブログの記事ID とリレーションするフィールドと、sample というフィールドを持つ仕様とします。

baserCMSが既にインストールされている前提で、事前にテーブルを作成しておきます。
テーブル名は、/app/Config/database.php を開き、「plugin」プロパティの「prefix」というキーで、プラグインのプレフィックスを確認し、下記のようにします。

[プレフィックス]sample_fields 
(例)mysite_pg_sample_fields

 

テーブル内のフィールドは下記のようにします。

  • id : int(8) ※ プライマリーキー&オートインクリメント
  • blog_post_id : int(8)
  • sample : text

なお、インストール時に、プラグイン用のテーブルを作成する方法は、プラグイン開発について を参考にしてください。

 

アウトプット

ブログ記事の詳細ページに、sample フィールドに保存した内容を表示します。
表示場所は、ブログ記事詳細ページのカテゴリ表示部分の左側とします。
今回は、「SampleFieldHelper」というヘルパークラスを作成し、それを利用して表示する仕様とします。

スクリーンショット 2015-05-03 18.20.06.png

 

プラグインのフォルダを作成する

それでは、実際に実装していきます。


/app/Plugin/ 配下に、今回作成するプラグインの名称のフォルダを作成します。
プラグインの名称は、キャメルケースである必要があります。

/app/Plugin/SampleField/

sample という入力欄を作成する

ブログ記事の入力フォームの下部に追加の入力欄を割り込ませるには、「FormHelper」のイベントである「Form.afterForm」を利用して実装します。
まず、プラグインのフォルダ内に、「Event」というフォルダを作成し、その中に、「SampleFiledHelperEventListener.php」というファイルを作成します。

/app/Plugin/SampleField/Event/SampleFieldHelperEventListener.php

 

このプログラムファイル内には、BcHelperEventListener を継承した、SampleFieldHelperEventListener というクラスを作成します。
今回、FormHelper が持っている「afterForm」というイベントを横取りして処理を割り込ませる為に、作成したクラスの中に「events」プロパティを定義し、その値に配列で、「Form.afterForm」という文字列を定義します。

実際の処理は、同じクラス内にメソッドを定義するのですが、イベント名からドットを除いた文字列をキャメルケースに変換したメソッド名にする必要があるので、「formAfterForm」というメソッドを定義し、第一引数を、CakeEvent型で定義します。

class SampleFieldHelperEventListener extends BcHelperEventListener {
    public $events = array(
        'Form.afterForm'
    );
    public function formAfterForm(CakeEvent $event) {

    }
}

formAfterForm というメソッドは、引数である、CakeEvent の data プロパティに、「fields」という配列を持ち、その配列に対し、「title」と「input」というキーを持った配列を追加すると、フォームの下部に、項目を追加する仕様となっています。

今回は、title に「sample」という文字列を代入し、input に 「SampleFiled.sample」という name 属性のテキストエリアを代入する仕様として作成します。
更新時に、対象レコードを特定する為、 hidden タグで、「SampleFiled.id」も出力しておきます。
また、このままでは、baserCMS管理画面内の全てのフォームに、sample という項目が表示されてしまうので、ブログ記事の編集フォームだけに表示するように、判定を入れます。

    public function formAfterForm(CakeEvent $event) {
        // フォームのIDが、BlogPostForm の場合のみ処理を実行
        if($event->data['id'] == 'BlogPostForm') {
            // BcFormHelperを利用する為、Viewを取得
            $View = $event->subject();
            // カスタムフィールドを追加
            $event->data['fields'][] = array(
                'title'    => 'sample',
                'input'    => 
                    $View->BcForm->input('SampleField.id', array('type' => 'hidden')) . 
                    $View->BcForm->input('SampleFiled.sample', array('type' => 'textarea'))
            );
        }
    }

 

保存処理を実装する

ブログ記事を保存する際に、SampleField プラグインの sample フィールドも同時に保存する為には、ブログ記事の保存直後のタイミングで、処理を割りこませます。
sample フィールドのデータを保存する為に、SampleField というモデルを作成します。

保存用モデルを作成する

SampleField プラグインのデータを取り扱う SampleField モデルを作成します。
まず、プラグインのフォルダ内に Model というフォルダを作成し、その中に、「SampleField.php」をというファイルを作成します。

/app/Plugin/SampleField/Model/SampleField.php

 

今回、バリデーション等は実装しないので、中身は空で構いません。

class SampleField extends BcPluginAppModel {}

 

SampleField モデルの保存処理を割りこませる

ブログ記事の保存直後のタイミングで、処理を割りこませるには、モデルが持つイベント「Blog.BlogPost.beforeSave」を利用して実装します。
まず、プラグインの「Event」フォルダの中に、「SampleFiledModelEventListener.php」というファイルを作成します。

/app/Plugin/SampleField/Event/SampleFieldModelEventListener.php

 

このプログラムファイル内には、BcModelEventListener を継承した、SampleFieldModelEventListener というクラスを作成します。
作成したクラスの中に「events」プロパティを定義し、その値に配列で、「Blog.BlogPost.afterSave」という文字列を定義します。

実際の処理を記述する為に、SampleFieldHelperEventListener の際と同様に、イベント名からドットを除いた文字列をキャメルケースに変換したメソッド名として「blogBlogPostAfterSave」というメソッドを定義し、第一引数を、CakeEvent型で定義します。

class SampleFieldModelEventListener extends BcModelEventListener {
    public $events = array(
        'Blog.BlogPost.afterSave'
    );
    public function blogBlogPostAfterSave(CakeEvent $event) {

    }
}

これで blogBlogPostAfterSave メソッドがブログ記事の保存直後に呼び出されるようになりましたので、呼び出し元のモデルを参照した上で、保存が完了したブログ記事のデータを取得し、外部キーを設定した上で sample フィールドを保存します。
ただし、この状態では、ブログ記事に関連づけて sample フィールドを読み出す処理が記述されていないので、保存しても入力欄には保存した値が表示されませんので注意が必要です。

    public function blogBlogPostAfterSave(CakeEvent $event) {
        // 呼び出し元のモデルを取得
        $BlogPost = $event->subject();
        // 保存が完了したブログ記事データを取得
        $data = $BlogPost->data;
        // ブログのプライマリーキーを外部キーとして設定
        $data['SampleField']['blog_post_id'] = $data['BlogPost']['id'];
        // SampleField モデルを初期化
        $SampleField = ClassRegistry::init('SampleField.SampleField');
        // データベースに保存
        $SampleField->save($data['SampleField']);
        return true;
    }

 

ブログ記事に関連づけて sample フィールドを読み出す

ブログ記事の読み出しの際に関連づけて sample フィールドを読み出すには、モデルが持つイベント「Blog.BlogPost.beforeFind」を利用して実装します。先ほど利用した「SampleFiledModelEventListener.php」というファイルに処理を追記します。

既に定義されている「events」プロパティの配列に、「Blog.BlogPost.beforeFind」という文字列を追加で定義します。

実際の処理を記述する為に、SampleFieldHelperEventListener の際と同様に、イベント名からドットを除いた文字列をキャメルケースに変換したメソッド名として「blogBlogPostBeforeFind」というメソッドを定義し、第一引数を、CakeEvent型で定義します。

これで blogBlogPostBeforeFind メソッドがブログ記事の読み出し直前に呼び出されるようになりましたので、CakePHPのアソシエーション機能を利用して、呼び出し元のモデルに SampleField モデルを1対1の関係で関連付けます。
この処理を実装する事で、ブログ記事の参照画面でも、sample フィールドを読み出す事ができるようになります。

    public function blogBlogPostBeforeFind(CakeEvent $event) {
        // 呼び出し元のモデルを取得
        $BlogPost = $event->subject();
        // BlogPost に SampleField を1対1の関係で関連づける
        $BlogPost->bindModel(array('hasOne' => array(
            'SampleField' => array(
                'className' => 'SampleField.SampleField',
                'foreignKey' => 'blog_post_id',
            )
        )), false);    
    }

 

プラグインをインストールする

このタイミングで、sample フィールドの保存動作を確認する為、プラグインを有効化しておきます。
ブラウザよりプラグイン管理を開くと、SampleField プラグインが認識されていますので、インストールを実行し、次の2点を確認します。

  • ブログ記事の編集画面に、sample という入力欄が表示されているか
  • sample 入力欄に文字列を入力し保存ボタンをクリックして保存できるか

 

ブログ記事の詳細ページに、sample フィールドを表示する

これまでの実装で、フロントサイドのブログ記事ページでも、既に sample フィールドを参照できるようになっていますが、今回は、ヘルパーを作成して、記述しやすいようにします。

ヘルパーを作成する

ビュー側で sample フィールドを出力する為の  SampleField ヘルパーを作成します。
まず、プラグインのフォルダ内に「View」というフォルダを作成し、さらにその中に「Helper」というフォルダを作成し、その中に「SampleFieldHelper.php」をというファイルを作成します。

/app/Plugin/SampleField/View/Helper/SampleFieldHelper.php

 

このプログラムファイル内には、「AppHelper」を継承した、「SampleFieldHelper」というクラスを作成します。
そしてクラス内に、「sample」という、sample フィールドを参照して出力するだけのシンプルな処理を記述します。
これだけでは、SampleFieldHelper を初期化する処理が記述されていませんので、ヘルパー自体を利用する事ができませんので注意が必要です。

class SampleFieldHelper extends AppHelper {
    public function sample($post) {
        echo $post['SampleField']['sample'];
    }
}

 

ヘルパーを利用できるように定義する

ヘルパーを利用する為には、コントローラーでヘルパーの定義が必要です。
コントローラーが持つイベント「startup」を利用して、ヘルパーの定義を追加します。

まず、プラグインの「Event」フォルダの中に、「SampleFiledControllerEventListener.php」というファイルを作成します。

/app/Plugin/SampleField/Event/SampleFiledControllerEventListener.php

 

このプログラムファイル内には、BcControllerEventListener を継承した、SampleFiledControllerEventListener というクラスを作成します。
作成したクラスの中に「events」プロパティを定義し、その値に配列で、「Blog.Blog.startup」という文字列を定義します。

実際の処理を記述する為に、SampleFieldHelperEventListener の際と同様に、イベント名からドットを除いた文字列をキャメルケースに変換したメソッド名として「blogBlogStartup」というメソッドを定義し、第一引数を、CakeEvent型で定義します。

class SampleFieldControllerEventListener extends BcControllerEventListener {
    public $events = array(
        'Blog.Blog.startup'
    );
    public function blogBlogStartup(CakeEvent $event) {

    }
}

これで blogBlogStartup メソッドがコントローラーのアクションが呼び出される直前に呼び出されるようになりましたので、呼び出し元のコントローラーを参照した上で、ヘルパーの定義を追加します。
これでビューテンプレートにおいて SampleFieldHelper が利用できるようになりました。

    public function blogBlogStartup(CakeEvent $event) {
        $Controller = $event->subject();
        $Controller->helpers[] = 'SampleField.SampleField';
    }

 

ビューで sample フィールドをヘルパーを利用して表示する

最後にブログ記事の詳細ページで、先ほど作成したヘルパーを利用して、sample フィールドを表示します。
ブログ記事の詳細ページ用のテンプレートは、次のパスのファイルとなります。

/app/webroot/theme/theme-name/Blog/default/single.php

 

任意の位置に、次のように記述します。

<?php $this->SampleField->sample($post) ?>

今回は、初期テーマとして、最初からパッケージに入っている nada-icons を利用しましたので、次のようになりました。

<?php
/**
 * ブログ詳細ページ
 */
$this->BcBaser->css('admin/colorbox/colorbox', array('inline' => false));
$this->BcBaser->js('admin/jquery.colorbox-min-1.4.5', false);
$this->BcBaser->setDescription($this->Blog->getTitle() . '|' . $this->Blog->getPostContent($post, false, false, 50));
?>

<script type="text/javascript">
$(function(){
    if($("a[rel='colorbox']").colorbox) $("a[rel='colorbox']").colorbox({transition:"fade"});
});
</script>

<h2 class="contents-head">
    <?php $this->Blog->title() ?>
</h2>
<h3 class="contents-head">
    <?php $this->BcBaser->contentsTitle() ?>
</h3>

<div class="eye-catch">
    <?php echo $this->Blog->eyeCatch($post) ?>
</div>

<div class="post">
    <?php $this->Blog->postContent($post) ?>
    <div class="meta"> 
        <span class="date">
            <?php $this->Blog->postDate($post) ?>
        </span>
        <!-- sample フィールド を追加 -->
        <span class="sample-field">
            <?php $this->SampleField->sample($post) ?>
        </span>
        <span class="category">
            <?php $this->Blog->category($post) ?>
            &nbsp;
            <?php $this->Blog->author($post) ?>
        </span>
    </div>
    <?php $this->BcBaser->element('blog_tag', array('post' => $post)) ?>
</div>

<div id="contentsNavi">
    <?php $this->Blog->prevLink($post) ?>
    &nbsp;  &nbsp;
    <?php $this->Blog->nextLink($post) ?>
</div>

<?php $this->BcBaser->element('blog_comments') ?>

実際に作ってみてうまくいかない場合は、サンプルをダウンロードし比較してみてください。

 

まとめ

baserCMSでは、フォルダ構成や命名規則などのルールを少し覚える必要がありますが、体系的に整理されていく前提となるので比較的メンテナンス性が高いプラグインの開発ができる事ができます。これは、「設定より規約」という思想を持つ CakePHP というフレームワークを採用している事による恩恵となります。

今回、「イベント」という仕組みを利用して、baserCMSの本体機能の振る舞いを変えるプラグインの開発を行いました。baserCMSには、他にも様々なイベントが用意されています。今回の記事を参考に、皆さんも baserCMSのプラグイン開発に挑戦してみてはいかがでしょうか。

 

 

江頭 竜二

江頭 竜二  - 株式会社キャッチアップ / 代表取締役

1973年佐賀県生まれ、佐賀育ち。就職後は福岡へ。
27歳で他業種よりIT関連会社へ転職。その後、2007年に独立。
5年間のフリーランスを経験後、2012年1月、株式会社キャッチアップを立ち上げ、Webシステム(CMS系)を中心とした提案、開発に携る。
ユーザビリティ・運営を意識した開発を心がけている。
フリーランス中に参加したコミュニティ活動を通じて、2010年に自身が開発していたCMS「baserCMS」をオープンソースとして公開。
現在は、baserCMSのコミュニティであるbaserCMSユーザー会を運営し、全国的な活動を行なっており、
2014年には、baserCMSを継続的に普及促進、機能改善していく為、特定非営利活動法人ベーサー・ファウンデーションを設立。

この人の関連サイト