開発

目次

a-blog cmsのモジュール


a-blog cmsのモジュール

a-blog cmsは GETモジュールPOSTモジュール という独自単位のPHPプログラムによって動作します。

GETモジュール

テンプレートを通して情報を表示するためのプログラムです。テンプレート内で表示制御のテンプレートタグとして動作します。

  • データベースの情報を表示する

  • XML生成用に加工して表示する

  • 外部データを取得して表示する

<html>
<body>
<!-- BEGIN_MODULE Hoge -->
(プログラムの出力結果)
<!-- END_MODULE Hoge -->
</body>
</html>

詳細はGETモジュールの開発を参照してください。

POSTモジュール

入力・操作に対してアクションを起こすためのプログラムです。送信されるフォームデータのキー(name属性)に ACMS_POST_{POSTモジュール名} を含めれば動作します。これにより、submitボタンだけでなくJavaScriptからフォームを送信する場合にも利用できます。

  • フォームの情報をデータベースに記録する

  • ボタンを押した後に指定したページに遷移する

  • ファイルをアップロードする

<form action="" method="post">
<input type="submit" name="ACMS_POST_Fuga" value="Fugaモジュールを実行" />
</form>
const csrfToken = window.csrfToken ?? document.querySelector('meta[name="csrf-token"]')?.content;
const formData = new FormData();
formData.append('ACMS_POST_Fuga', 'Fugaモジュールを実行');
formData.append('formToken', window.csrfToken);
// 必要に応じて他のフィールドを追加
// formData.append('field1', '値');

const response = await fetch(document.location.href, {
  method: 'POST',
  body: formData,
  headers: {
     'X-CSRF-Token': csrfToken,
     'X-Requested-With': 'XMLHttpRequest',
  },
});

詳細はPOSTモジュールの開発を参照してください。

V2 GET モジュール

Twigテンプレート と連携する新しい形式のGETモジュールです。従来のGETモジュールと異なり、連想配列を返す ことでTwig側で柔軟に表示を制御できます。

項目

従来のGETモジュール

V2 GETモジュール

基底クラス

ACMS_GET

Acms\Modules\Get\V2\Base

戻り値

文字列(HTML等)

配列(構造化データ)

呼び出し

<!-- BEGIN_MODULE -->

Twig の module() 関数

{% set result = module('V2_Sample') %}
<p>{{ result.moduleTest }}</p>

詳細は V2 GETモジュールの開発 を参照してください。


ディレクトリ構造

パス

用途

/php/ACMS/GET/

GETモジュール

/php/ACMS/POST/

POSTモジュール

/extension/acms/

独自モジュール(GET/POST/V2)

GETモジュールはGETディレクトリ内に、POSTモジュールはPOSTディレクトリ内に配置します。独自V2モジュールは extension/acms/Modules/Get/V2/ に配置します。

extension/acms/
├── Modules/Get/V2/    # 独自 V2 GETモジュール
├── GET/               # 独自 GETモジュール
├── POST/              # 独自 POSTモジュール
├── Hook.php
├── Corrector.php
└── Validator.php

GETモジュールの開発

GETモジュールの雛形

<?php

namespace Acms\Custom\GET;

use ACMS_GET;
use Template;

/**
 * extension/acms/GET/Sample.php
 *
 * テンプレート上では、標準のGETモジュールと同様に、
 * '<!-- BEGIN_MODULE Sample --><!--END_MODULE Sample -->' で呼び出されます。
 */
class Sample extends ACMS_GET
{
    function get()
    {
        return 'sample sample';
    }
}

GETモジュールの規約

クラス

GETモジュールとして動作させるために、ACMS_GETを継承してください。
ファイルのパスと命名規則はPSR4に従います。(ただし下位互換性を保つため一部したがっていない場合があります)

**extension/acms の名前空間は Acms\Custom になります。**

この命名規則によりクラスがオートロード(autoload)されますので、requireなどは必要ありません。

extension/acms/GET/Sample.php の場合

namespace Acms\Custom\GET;
use ACMS_GET;

class Sample extends ACMS_GET {
}

extension/acms/GET/Own/Sample.php の場合

namespace Acms\Custom\GET\Own;
use ACMS_GET;

class Sample extends ACMS_GET {
}

出力

GETモジュールは呼び出されるとgetメソッドを実行します。最終的にgetメソッド内で、returnした文字列がモジュール内で出力されます。Smartyなど他のテンプレートエンジンと組み合わせたり、外部から情報を取得したりして情報を出力できます。

returnする文字列にはHTMLタグなどが含まれていても構いません。テンプレートとプログラムを分離させる必要が無い場合は、直接HTMLコンテンツを出力することもできます。

実行例

冒頭の雛形を実行する際のテンプレートと、その実行結果を例示します。GETモジュールは、BEGIN_MODULEから始まり、ENDMODULEで終わる、一連のテンプレートタグによって呼び出されます。テンプレート上のモジュール名は Acms\Custom 以降のクラス名を **(アンダーバー)** 繋ぎにしたものになります。

例: Acms\Custom\GET\Own\Sample -> Own_Sample

テンプレート

<html>
<body>
    <p><!-- BEGIN_MODULE Own_Sample --><!-- END_MODULE Oen_Sample --></p>
</body>
</html>

実行結果

<html>
<body>
    <p>sample sample</p>
</body>
</html>

ページ情報を取得する

URLコンテキスト(参照)で表されるページ情報のうち各種IDをGETモジュール内で取得する方法を説明します。

GETモジュール内でページ情報のうち、例えばエントリーID(以下EID)を取得することができれば、そのモジュールがどのエントリーから呼び出されたかによって参照する情報を変えることができます。基本的には、Entry_BodyやEntry_Headlineなどのモジュールもそのようにページ情報を取得することで、表示すべき情報を決定しデータベースを参照しています。

スコープの設定

下記のようにプロパティを設定することで、$this->bid, $this->cid, $this->eidとして、表示中のページが持つID情報を取得できます。

public $_scope = array(
    'bid' => 'global',
    'cid' => 'global',
    'eid' => 'global',
);

表示中ページの各種IDを参照するサンプル

<?php

namespace Acms\Custom\GET;

use ACMS_GET;

class Hoge extends ACMS_GET
{
    public $_scope = array(
        'bid' => 'global',
        'cid' => 'global',
        'eid' => 'global',
    );

    function get()
    {
        $string[] = 'Global Vars';
        $string[] .= "BID = $this->bid";
        $string[] .= "CID = $this->cid";
        $string[] .= "EID = $this->eid";

        return nl2br(implode("\r\n", $string));
    }
}

出力結果

<!-- bid = 1, cid = 2, eid = 5 の場合 -->
Global Vars<br />
BID = 1<br />
CID = 2<br />
EID = 5

取得できる情報

GETモジュールで現在表示中のページより取得できる情報には以下のものがあります。

プロパティ

説明

bid

ブログID

http://sample.com/blog/

uid

ユーザーID

http://sample.com/uid/1/profile.html

cid

カテゴリーID

http://sample.com/news/

eid

エントリーID

http://sample.com/news/123.html

keyword

キーワード

http://sample.com/keyword/検索文字/
http://sample.com/?keyword=検索文字

tag

タグ

http://sample.com/tags/aaa/

field

フィールド

http://sample.com/field/station/あの駅/

start

検索の始まり日時

http://sample.com/2017-01-01/-/2017-03-03/

end

検索の終わり日時

http://sample.com/2017-01-01/-/2017-03-03/

page

ページ番号

http://sample.com/page/2/

order

並び順

http://sample.com/order/datetime-desc/

POSTモジュールの開発

POSTモジュールの雛形

<?php

namespace Acms\Custom\POST;

use ACMS_POST;

/**
 * extension/acms/POST/Sample.php
 *
 * テンプレート上では、標準のPOSTモジュールと同様に、
 * '<input type="submit" name="ACMS_POST_Sample" value="送信" />' で呼び出されます。
 */
class Sample extends ACMS_POST
{
    function post()
    {
        return $this->Post;
    }
}

POSTモジュールの規約

クラス

POSTモジュールとして動作させるために、ACMS_POSTを継承してください。ファイルのパスと命名規則はPSR4に従います。(ただし下位互換性を保つため一部したがっていない場合があります)

またこの命名規則によりクラスがオートロード(autoload)されますので、requireなどは必要ありません。

extension/acms/POST/Sample.php の場合

<?php

namespace Acms\Custom\POST;

use ACMS_POST; 

class Sample extends ACMS_POST
{
}

extension/acms/POST/Own/Sample.php の場合

<?php

namespace Acms\Custom\POST\Own;

use ACMS_POST; 

class Sample extends ACMS_POST
{
}

実行

POSTモジュールは呼び出されるとpostメソッドを実行します。postメソッド内で処理を実行した後には、最後に$this->Postをreturnします。これは規約上のスペルです。次に起動するGETモジュールとの情報の受け渡しに利用されますが、ここでは言及しません。

制約

POSTモジュール内では、echoやprintなど文字列を直接出力することはしません。表示(View)に関わる部分はGETモジュールで行います。

V2 GET モジュールの開発


V2 GETモジュールとは

V2 GETモジュールは、Twig テンプレート と連携する新しい形式の GET モジュールです。従来の GET モジュールと異なり、連想配列を返す ことで Twig 側で柔軟に表示を制御できます。

項目

従来の GET モジュール

V2 GET モジュール

基底クラス

ACMS_GET

Acms\Modules\Get\V2\Base

戻り値

文字列(HTML 等)

配列(構造化データ)

テンプレート

独自テンプレートエンジン

Twig

呼び出し

<!-- BEGIN_MODULE -->

module() 関数


ディレクトリ構造

独自 V2 モジュールは extension/acms/Modules/Get/V2/ に配置します。名前空間は Acms\Custom\Modules\Get\V2、オートロード済みのため require は不要です。

extension/acms/Modules/Get/V2/
└── Sample.php

雛形と規約

雛形

<?php

namespace Acms\Custom\Modules\Get\V2;

use Acms\Modules\Get\V2\Base;

/**
 * extension/acms/Modules/Get/V2/Sample.php
 *
 * テンプレート上では、Twig の module() 関数で呼び出されます。
 */
class Sample extends Base
{
    public function get(): array
    {
        return [
            'moduleTest' => 'Sample',
        ];
    }
}

クラス

  • Acms\Modules\Get\V2\Base を継承する

  • ファイルのパスと命名規則は PSR4 に従う

  • extension/acms の名前空間は Acms\Custom\Modules\Get\V2

モジュール名とクラス名の対応

テンプレートで使用するモジュール名は アンダースコア区切り です。モジュール名の _ は名前空間・クラス名の \ に対応します。

モジュール名

クラス名

ファイルパス

V2_Sample

Acms\Custom\Modules\Get\V2\Sample

Modules/Get/V2/Sample.php

V2_Own_Sample

Acms\Custom\Modules\Get\V2\Own\Sample

Modules/Get/V2/Own/Sample.php

extension/acms の独自モジュールは Acms\Custom\Modules 名前空間で解決され、標準モジュールより に検索されるため、同名で上書き可能です。

出力

get() メソッドで 配列 を return します。文字列は返しません。

public function get(): array
{
    return [
        'items' => [...],
        'count' => 10,
    ];
}

実行例

テンプレート(Twig)

{% set result = module('V2_Sample') %}
<p>{{ result.moduleTest }}</p>

実行結果

<p>Sample</p>

module() 関数

Twig テンプレートでは module() 関数で V2 モジュールを呼び出します。

{% set data = module(モジュール名, 識別子, コンテキスト) %}

引数

説明

第1引数

string

モジュール名(例: 'V2_Sample'

第2引数

string|null

モジュールIDの識別子。使わない場合は null

第3引数

array

外部コンテキストの指定

{% set result = module('V2_Sample', 'my-sample', {
    cid: 1,
    page: 1,
    limit: 10,
    keyword: null,
    tag: null,
    field: null,
    order: null,
    start: null,
    end: null
}) %}

モジュールID化

GETモジュールのID化 を参照してください


Base クラスのプロパティ

Acms\Modules\Get\V2\Base を継承したモジュールで利用できるプロパティです。

URL コンテキスト関連

プロパティ

説明

$this->bid

int|null

ブログID

$this->bids

int[]

ブログIDの配列

$this->cid

int|null

カテゴリーID

$this->cids

int[]

カテゴリーIDの配列

$this->eid

int|null

エントリーID

$this->eids

int[]

エントリーIDの配列

$this->uid

int|null

ユーザーID

$this->uids

int[]

ユーザーIDの配列

$this->page

int

ページ番号

$this->limit

int|null

取得件数

$this->keyword

string

キーワード

$this->tag

string

タグ

$this->tags

string[]

タグの配列

$this->field

string

フィールド検索

$this->Field

Field_Search

フィールド検索オブジェクト

$this->order

string

並び順

$this->start

string

検索開始日時

$this->end

string

検索終了日時

その他

プロパティ

説明

$this->Get

Field

GET パラメータ

$this->Post

Field_Validation

POST データ

$this->moduleContext

Field

モジュールコンテキスト

$this->mid

int|null

モジュールID

$this->mbid

int|null

モジュールIDのブログID

$this->identifier

string|null

モジュールID識別子


スコープと階層

スコープ(scopes)

$scopes で、URL コンテキストを localglobal で取得するかを制御します。

  • global: URL の情報を優先(表示ページのコンテキスト)

  • local: モジュールIDの設定やテンプレート指定を優先

protected $scopes = [
    'uid' => 'global',
    'cid' => 'global',
    'eid' => 'global',
    'keyword' => 'global',
    'tag' => 'global',
    'field' => 'global',
    'start' => 'global',
    'end' => 'global',
    'page' => 'global',
    'order' => 'global',
];

階層(axis)

$axis で、ブログ・カテゴリーの階層を指定します。

protected $axis = [
    'bid' => 'self',           // self | descendant-or-self | ancestor-or-self
    'cid' => 'self',
];

モジュールIDのコンフィグ

モジュールIDに紐づく設定を読み込むには loadModuleConfig() を使用します。

public function get(): array
{
    $config = $this->loadModuleConfig();
    $limit = (int) $config->get('my_limit', 10);
    $order = $config->get('my_order', 'datetime-desc');

    // ...
}

ヘルパーメソッド

基本パラメータをまとめて取得するには getBaseParams() を使用します。

$params = $this->getBaseParams();
// bid, bids, cid, cids, eid, eids, uid, uids, field, keyword, tag, tags,
// start, end, page, order, limit, blogAxis, categoryAxis を含む配列

その他

カスタムフィールド

モジュールIDでカスタムフィールドを有効にしている場合、buildModuleField() でフィールドデータを取得できます。$this->customFieldsEnabledtrue かつ $this->mid が設定されている場合に有効です。

$fields = $this->buildModuleField();

キャッシュ

$this->cacheLifetime が 0 より大きく、$this->identifier が設定されている場合、モジュールの実行結果がキャッシュされます。モジュールID使用時に自動的に有効化されます。

フック

V2 モジュール実行後には afterV2GetFire フックが呼ばれます。拡張アプリ等でフックを登録している場合は、レスポンス配列を加工できます。

// フックの引数: [ &$response, $this ]

管理画面でのデバッグ表示

管理者としてログインしている場合、実行結果に moduleInfo が付与されます。テンプレートで設定画面へのリンクなどを表示できます。

{{ include('/admin/module/setting.twig', { moduleInfo: result.moduleInfo }) }}

GETモジュールのID化


独自に開発した GET モジュール及び、V2 GET モジュールも、標準のビルドインモジュールと同様にモジュールID化できます。ここでは、モジュールID化の仕方と、モジュールID化によってどのような動作になるかを説明します。

モジュールID化できるようにする

まずモジュールID化に対応するため、モジュールID作成時に独自モジュールを選択できるようにします。

ご利用のテーマに以下のファイルを作成し、セレクト要素を記述します。

ファイル: /themes/ご利用テーマ/admin/module/select.user.html

従来のGETモジュールの場合

<optgroup label="独自モジュール">
    <option value="Sample"{name:selected#Sample}>独自モジュール(Sample)</option>
</optgroup>

V2 GETモジュールの場合

<optgroup label="独自モジュール">
    <option value="V2_Sample"{name:selected#V2_Sample}>独自モジュール(V2 Sample)</option>
</optgroup>

仕様(共通)

  • : テンプレートで呼び出すモジュール名と同じ値

  • 変数: {name:selected#セレクトの値}

この実装を行うことでモジュールIDの作成ができるようになります。

モジュールIDの作成

管理画面のモジュールID作成画面で、上記で追加した独自モジュールを選択し、識別子(identifier)を設定してモジュールIDを作成します。

従来のGETモジュールの呼び出し

標準テンプレートでは id 属性で識別子を指定します。

<!-- BEGIN_MODULE Sample id="sample" --><!-- END_MODULE Sample -->

V2 GETモジュールの呼び出し

Twig テンプレートでは module() の第2引数で識別子を指定します。

{% set result = module('V2_Sample', 'sample') %}

モジュールID化でのコンテキスト情報の取得

モジュールID化した時のコンテキスト情報の取得について解説します。従来のGETモジュールと V2 GET モジュールで、スコープの設定方法が異なりますが、引数・固定値によるコンテキストの取得ルールは共通です。

モジュールID化する前

モジュールIDを指定しない場合、スコープで global を設定していれば、$this->bid$this->cid$this->eid で表示ページの URL コンテキスト情報を取得できます。

従来のGETモジュール

public $_scope = [
    'bid' => 'global',
    'cid' => 'global',
    'eid' => 'global',
];

function get()
{
    return "BID = $this->bid, CID = $this->cid, EID = $this->eid";
}

V2 GETモジュール

protected $scopes = [
    'bid' => 'global',
    'cid' => 'global',
    'eid' => 'global',
];

public function get(): array
{
    return [
        'bid' => $this->bid,
        'cid' => $this->cid,
        'eid' => $this->eid,
    ];
}

モジュールID化した場合

モジュールIDを指定して呼び出した場合、そのままでは $this->cid$this->eid で表示ページのコンテキスト情報を取得できません。(bid だけはモジュールID化しても取得できます。)

URLコンテキスト情報を取得するには

モジュールID管理画面で、取得したい情報の引数項目にチェックを付けます。これにより URL コンテキストの情報を取得できるようになります。

固定値の設定

$this->cid$this->eid は通常 URL コンテキスト情報を取得しますが、モジュールID化すると、固定値を設定することで挙動が変わります。

モジュールID管理画面で固定値を設定した場合、表示している URL が別のカテゴリーや、トップページなどカテゴリー情報がない場合でも、常に $this->cid は固定値で設定したカテゴリーIDを返すようになります。

モジュールID化時のコンテキスト情報の取得まとめ(共通)

引数チェック

固定値

$this->cid などのプロパティ

あり

なし

URL の情報を取得

なし

あり

モジュールIDで設定した固定値を取得

あり

あり

URL に該当の情報があれば URL の情報を取得。ない場合は固定値を取得

表示ページのURLコンテキストを常に取得したい場合

モジュールIDの設定と関係なく表示ページの URL コンテキストを取得したい場合は、定数を利用します。

項目

定数

ブログID

BID

ユーザーID

UID

カテゴリーID

CID

エントリーID

EID

キーワード

KEYWORD

タグ

TAG

フィールド

FIELD

日時検索のスタート

START

日付検索のエンド

END

ページ番号

PAGE

並び順

ORDER

最大表示件数

LIMIT

// モジュールIDの設定に関係なく表示ページのカテゴリーIDを取得
$displayPageCid = CID;

従来のGETモジュールと V2 GETモジュールの違い(モジュールID化)

項目

従来のGETモジュール

V2 GETモジュール

スコープの設定

$_scope プロパティ

$scopes プロパティ

テンプレートでの呼び出し

<!-- BEGIN_MODULE Sample id="sample" -->

module('V2_Sample', 'sample')

戻り値

文字列

配列

テンプレートエンジン - 1(基礎)

a-blog cmsのテンプレートエンジン

a-blog cmsは独自のテンプレートエンジンを備え、標準のGETモジュールはすべてこのテンプレートエンジンを通して実装されています。ここではそのTemplateクラスの使用方法を説明します。

Templateクラス

Templateクラスはテンプレートをもとに、与えられたデータに応じて情報を加工して出力します。

$Tpl = new Template($this->tpl);

$this->tpl

$this->tpl には、テンプレート上における、BEGIN_MODULE〜END_MODULEまでの間の文字列が自動で格納されています。

<html>
<body>
    <!-- BEGIN_MODULE Sample_UseTpl -->
    <!-- ここから -->
    <p>{hoge} and {fuga}</p>
    <!-- BEGIN foo --><p>{bar}</p><!-- END foo -->
    <!-- ここまでが$this->tplには入ってくる -->
    <!-- END_MODULE Sample_UseTpl -->
</body>
</html>

addメソッド

addメソッドは、ブロックや変数をテンプレートに加えるメソッドです。

  • ブロック: ある範囲を表示するためや、繰り返し(ループ)をする テンプレート要素
  • 変数: 動的に表示を変わってくるテンプレート要素

サンプル

// get module
$Tpl = new Template($this->tpl);

// ブロックの追加
$Tpl->add('notFound');

// 変数の追加
$Tpl->add(null [
  'hoge' => 'hello world.'
]);

// html
<h1>{hoge}</h1>

<!-- ブロックの追加がされていないと以下の表示はされない -->
<!-- BEGIN notFound -->404 Not Found<!-- END notFound -->

getメソッド

getメソッドは、加えられた情報をもとにテンプレート処理を解決し、その結果の文字列を取得するメソッドです。通常はreturnするときにそのまま呼び出します。

サンプル

function get()
{
  $Tpl = new Template($this->tpl);

  $Tpl->add('notFound');
  $Tpl->add(null, [
    'hoge' => 'hello world.'
  ]);

  return $Tpl->get();
}

ACMS_Correctorクラス (校正オプションの利用)

モジュールのテンプレート内で校正オプションを利用する場合は、ACMS_Correctorクラスのインスタンスを同時に渡します。 これにより以下のように変数に対して校正オプションを利用できるようになります。

サンプル

$Tpl = new Template($this->tpl, new ACMS_Corrector());

$Tpl->add(null, [
  'hoge' => date('Y-m-d H:i:s'),
]);

// html
<p>{hoge}[datetime('Y年m月d日 H時i分)]</p>

// 表示
<p>2017年01月01日 12時59分</p>

全体のサンプル(まとめ)

テンプレート(HTML形式)

<html>
<body>
    <!-- BEGIN_MODULE Sample_UseTpl -->
    <p>{hoge} and {fuga}</p>
    <!-- BEGIN foo --><p>{bar}</p><!-- END foo -->
    <!-- END_MODULE Sample_UseTpl -->
</body>
</html>

Sample_UseTplモジュール

<?php

namespace Acms\Custom\GET\Sample;

use ACMS_GET;
use Template;
use ACMS_Corrector;

/*
 * path: extension/acms/GET/Sample/UseTpl.php
 */
class UseTpl extends ACMS_GET
{
  function get()
  {
    // Templateクラス
    $Tpl  = new Template($this->tpl, new ACMS_Corrector());

    // <!-- BEGIN foo --> ~ <!-- END foo --> の中の {bar} にテキストを代入
    $Tpl->add('foo', array('bar' => 'fooブロックの中のbar変数'));

    // ブロックを介さない、モジュール直下の変数 {hoge} と {fuga} にテキストを代入
    $Tpl->add(null, [
      'hoge' => 'ほげ', 'fuga' => 'ふが'
    ]);

    // テンプレートを処理済みの文字列としてreturn
    return $Tpl->get();
  }
}

実行結果

<html>
<body>
    <p>ほげ and ふが</p>
    <p>fooブロックの中のbar変数</p>
</body>
</html>

テンプレートエンジン - 2(ループブロック、入れ子のブロック)

ブロック制御

前回はTemplateクラスのブロックと変数の制御について説明しました。今回はのようなループするブロックと、入れ子構造になっているブロックの出力について説明します。

ループの追加を行う

Templateクラスのaddメソッドは次のような引数を持ちます。

$Tpl->add( ブロック名(文字列), ブロック内変数( 連想配列 ) );

以下のようにaddメソッドで同じ名前のブロックを繰り返し追加することでループ構造を作ります。

テンプレート (HTML形式)

<html>
<body>
    <!-- BEGIN_MODULE Sample_Loop -->
    <ul>
        <!-- BEGIN data:loop -->
        <li>id : {id} {name} ( {kana} ) </li>
        <!-- END data:loop -->
    </ul>
    <!-- END_MODULE Sample_Loop -->
</body>
</html>

Sample_Loopモジュール

<?php

namespace Acms\Custom\GET\Sample;

use ACMS_GET;
use Template;
use ACMS_Corrector;

class Loop extends ACMS_GET
{
    function get()
    {
        $Tpl = new Template($this->tpl, new ACMS_Corrector());

        $loopData = array();
        $loopData[] = array(
            'id'       => '1',
            'name'     => '山田太郎',
            'kana'     => 'やまだたろう',
        );

        $loopData[] = array(
            'id'       => '2',
            'name'     => '鈴木次郎',
            'kana'     => 'すずきじろう',
        );

        $loopData[] = array(
            'id'       => '3',
            'name'     => '佐藤三郎',
            'kana'     => 'さとうさぶろう',
        );

        foreach ( $loopData as $loop ) {
            $Tpl->add('data:loop', $loop);
        }

        return $Tpl->get();
    }
}

実行結果

<html>
<body>
    <ul>
        <li>id : 1 山田太郎 ( やまだたろう )</li>
        <li>id : 2 鈴木次郎 ( すずきじろう )</li>
        <li>id : 3 佐藤三郎 ( さとうさぶろう )</li>
    </ul>
</body>
</html>

入れ子の順番

ブロックの追加は、入れ子の中の深い位置のブロック(子にあたるブロック)から順に追加します。ブロックを配列で表現して追加するときは、配列の先頭が追加する目標のブロック名であり、以降は親のブロック名をあらわすことに注意してください。

入れ子のテンプレート

<html>
<body>
<!-- BEGIN_MODULE Sample_Nest -->
    <!-- BEGIN year -->
        <!-- BEGIN month -->
            <!-- BEGIN day -->
            <!-- END day -->
        <!-- END month -->
    <!-- END year -->
<!-- END_MODULE Sample_Nest -->
</body>
</html>

Sample_Nestモジュール

<?php

namespace Acms\Custom\GET\Sample;

use ACMS_GET;
use Template;
use ACMS_Corrector;

class Nest extends ACMS_GET
{
    function get()
    {
        $Tpl     = new Template($this->tpl, new ACMS_Corrector());

        $Tpl->add( array('day', 'month', 'year') );

        $Tpl->add( array('month', 'year') );

        $Tpl->add( 'year' );

        return $Tpl->get();
    }
}

入れ子構造のループを追加する

ひとつのループの中で更に別のループがあるような入れ子型のループを作成します。以下のように、ブロック名の引数を配列にすることでループの中に別のループを追加できます。この入れ子型のブロックの追加はループに限らず共通です。

以下はカテゴリーのループ構造の中に、エントリーのループが加わる処理の例です。

    foreach ( $categories as $category ) {
        foreach ( $entries as $entry ) {
            // entry:loopを追加
            $Tpl->add(array('entry:loop', 'category:loop'), $entry);
        }
        // category:loopを追加
        $Tpl->add('category:loop', $category);
    }

テンプレートエンジン - 3(render method)

ここまで、ブロックの制御の仕方や変数の追加の仕方などを説明してきましたが、Ver. 2.6.1で追加された render メソッドを利用するともっと簡単にテンプレートエンジンを扱うことができます。

まずは、今まで説明してきたやり方とrenderメソッドを使ったサンプルで同じものを出力してみて、どう違うか比較してみましょう。

出力するテンプレート(HTML)

<!-- BEGIN_MODULE Sample -->
<!-- BEGIN message -->
ブロックの表示
<!-- END message -->

<!-- BEGIN message2 -->
<p>{msg}</p>
<!-- END message2 -->  

<p>件数: {count}件</p>

<ul>
  <!-- BEGIN data:loop -->
  <li>{id}: {name}</li>
  <!-- END data:loop -->
</ul>
<!-- END_MODULE Sample -->

今までの方法

function get()
{
  $Tpl = new Template($this->tpl, new ACMS_Corrector());

  $data = [
    [
      'id' => 'aaa',
      'name' => '山田太郎',
    ],
    [
      'id' => 'bbb',
      'name' => '鈴木次郎',
    ],
    [
      'id' => 'ccc',
      'name' => '佐藤三郎',
    ],
  ];

  foreach ( $data as $i => $line ) {
    $line['data.i'] = ++$i;
    $Tpl->add('data:loop', $line);
  }

  $Tpl->add('message');
  $Tpl->add('message2', [
    'msg' => 'ブロック内の変数',
  ]);

  $Tpl->add(null, [
    'count' => count($data),
  ]);

  return $Tpl->get();
}

renderメソッドを使った方法

function get()
{
  $Tpl = new Template($this->tpl, new ACMS_Corrector());

  $data = [
    [
      'id' => 'aaa',
      'name' => '山田太郎',
    ],
    [
      'id' => 'bbb',
      'name' => '鈴木次郎',
    ],
    [
      'id' => 'ccc',
      'name' => '佐藤三郎',
    ],
  ];

  $obj = [
    'data' => $data,
    'count' => count($data),
    'message' => (object)[],
    'message2' => [
      'msg' => 'ブロック内の変数',
    ]
  ];

  return $Tpl->render($obj);
}

どうでしょうか? 今までのやり方は、その都度テンプレートを組み立てていくイメージですが、renderメソッドはデータの組み立てに集中し最後にrenderメソッドに渡すイメージです。

これにより、a-blog cms独特なテンプレートエンジンのルールを覚えなくても、データを連想配列または、オブジェクトで用意するだけで簡単に実装ができるようになります。

render 仕様

それでは render メソッドの詳しい仕様を見ていきましょう。

引数

まず、renderメソッドに渡す引数は、連想配列 もしくは オブジェクト に対応しています。

連想配列形式

$data = [
  'hoge1' => 'value1',
  'hoge2' => 'value2',
];

return $Tpl->render($data);

オブジェクト形式

$data = new stdClass();

$data->hoge1 = 'value1';
$data->hoge2 = 'value2';

return $Tpl->render($data);

変数

変数になるデータは連想配列の値、もしくはオブジェクトのプロパティの値が、配列、オブジェクトでないスカラー型の場合に変数になります。 注意点として、論理値(boolean)は 1 or 0 に変換されて表示されますのでお気をつけください。

変数になるデータ形式表示
論理値 (boolean)1 or 0
整数 (integer)そのまま
浮動小数点数 (float, double も同じ)そのまま
文字列 (string)そのまま

ブロック

ブロックは、連想配列の値、もしくはオブジェクトのプロパティの値が、連想配列またはオブジェクトの場合にブロックになります。

その値が空のオブジェクトや連想配列の場合は、ブロックのみの出力になり、データ(連想配列やプロパティ)がある場合は、そのブロック内で変数が利用できます。

<!-- 例1 変数をもったブロックの出力(連想配列の場合) -->
<!-- BEGIN hoge -->{val}<!-- END hoge -->
$data = [
  'hoge' => [
    'val' => 'value',
  ]
];

<!-- 例2 空のブロック出力(オブジェクトの場合)-->
<!-- BEGIN hoge -->hoge<!-- END hoge -->
$data = new stdClass();
$data->hoge = new stdClass();

ループブロック

ループブロックは、連想配列の値もしくはオブジェクトのプロパティの値が、配列の場合にループブロックになります。連想配列でない ところに注意してください。

また、ループブロック名は、 連想配列のkey名:loop になります。

$data = [
  [
    'id' => 'aaa',
    'name' => '山田太郎',
  ],
  [
    'id' => 'bbb',
    'name' => '鈴木次郎',
  ],
  [
    'id' => 'ccc',
    'name' => '佐藤三郎',
  ],
];

return $Tpl->render(['data' => $data]);

// html
<!-- BEGIN data:loop -->
<li>{id}: {name}</li>
<!-- END data:loop -->

上の例では配列の中身が連想配列だったので、そのまま連想配列が変数として利用できました。では、配列の中身がスカラー型だった場合はどうでしょうか。

$data = ['山田太郎', '鈴木次郎',  '佐藤三郎'];

return $Tpl->render(['data' => $data]);

// html
<!-- BEGIN data:loop -->
<li>{data}</li>
<!-- END data:loop -->

この場合は、上記のコードのように連想配列のkey名がそのままループ内の変数として利用できます。

特殊なブロック、変数

さらにループブロック内では、自動的に生成される特殊なブロックと変数を利用できます。

添字変数

ループ内で {連想配列のkey名.i} という変数が使えるようになります。これは、ループ回数を出力するものになります。

$data = ['山田太郎', '鈴木次郎',  '佐藤三郎'];

return $Tpl->render(['data' => $data]);

// html
<!-- BEGIN data:loop -->
<li>{data.i}:{data}</li>
<!-- END data:loop -->

// 結果
<li>1:山田太郎</li>
<li>2:鈴木次郎</li>
<li>3:佐藤三郎</li>
glueブロック

ループ回数 - 1回出力される glueブロックも出力されるようになります。ブロック名は 連想配列のkey名:glue です。使いどころとしては配列をカンマでつなげたい場合などに利用できます。

$data = ['山田太郎', '鈴木次郎',  '佐藤三郎'];

return $Tpl->render(['data' => $data]);

// html
<!-- BEGIN data:loop -->
  <!-- BEGIN data:glue -->, <!-- END data:glue -->{data}
<!-- END data:loop -->

// 結果
山田太郎, 鈴木次郎,  佐藤三郎

Ver. 2.6.1以上をご利用の場合は、renderメソッドで独自モジュールを作っていくほうが、簡潔に記述できますのでrederメソッドをお勧めします。

データベース関係クラスの使用(1) DB編

DBクラス

DBクラスは、a-blog cmsで使用しているデータベースへの接続を支援するクラスです。

インスタンスの作成

// DBクラス
DB = DB::singleton(dsn());

dsn関数は、a-blog cmsの組み込み関数で、config.server.phpに保存されているデータベース情報を、連想配列で返します。以降のサンプルで紹介されている箇所以外で、使用することはほとんどありません。

クエリーの発行

基本的なクエリーの発行と、結果を全件取得するサンプルを下記に示します。

$DB = DB::singleton(dsn());
$q = '`SELECT * FROM `acms_entry` LIMIT 0, 10';

/**
 * query
 * @param string $query
 * @param string $mode
 * @return mixed $response
 */

// クエリーの結果を全件取得する
$all = $DB->query($q, 'all');

クエリー発行の各モードの挙動

queryメソッドは、複数のモードを持ちます。それぞれのモードで、返り値が異なります。

all - 結果から全行を取得

結果を多次元配列として、全行取得します。

$q = '`SELECT * FROM `acms_entry` LIMIT 0, 10';
$all = $DB->query($q, 'all');

var_dump($all);
/*
dumpの結果

array(10)(
    [0] => array(21)(
        'entry_id' => 1,
        'entry_title' => 'サンプルレコード',
        'entry_code' => 'entry-1.html',
        ...
    ),
    [1] => array(21)(
        'entry_id' => 2,
        'entry_title' => 'サンプルレコード2',
        'entry_code' => 'entry-2.html',
        ...
    ),
    [2] => array(21)(
        'entry_id' => 3,
        'entry_title' => 'サンプルレコード3',
        'entry_code' => 'entry-3.html',
        ...
    ),
    ...
);
*/

row - 結果から1行を取得

結果から1行のみ取得します。結果が複数行だった場合は、先頭の1行を連想配列で返します。

$q = '`SELECT * FROM `acms_entry` WHERE `entry_id` = 1';
$row = $DB->query($q, 'row');

var_dump($row);
/*
dumpの結果

array(21)(
    'entry_id' => 1,
    'entry_title' => 'サンプルレコード',
    'entry_code' => 'entry-1.html',
    ...
)
*/

one - 結果から1つの値を取得

結果から1つの値を取得します。結果が複数列をもつ場合は、先頭列の値を取得します。また、結果が複数行であった場合は、先頭の1行が対象行となります。

$q = '`SELECT `entry_title` FROM `acms_entry` WHERE `entry_id` = 1';
$one = $DB->query($q, 'one');

var_dump($row);
/*
dumpの結果 

string "サンプルレコード"
*/

fetch - 結果を1行ずつ取得

結果を1行ずつ取得できるように、クエリーの結果をDBインスタンス内で保持します。保持された結果は、fetchメソッドで1行ずつ取得します。

fetchメソッドの返り値はrowモードと同じ挙動を示しますが、取得すべき結果が無くなった場合は、falseを返します。

$q = '`SELECT * FROM `acms_entry` LIMIT 0, 10';
$DB->query($q, 'fetch');

/**
 * fetch
 * @param string $query
 * @return array $row
 */
while ( $row = $DB->fetch($q) ) {
   echo $row['entry_title']."\r\n";
}

/*
echo の結果

サンプルレコード
サンプルレコード2
サンプルレコード3
...
..
.

*/

exec - クエリーの成否をbooleanで取得

クエリーの成否のみをbooleanで返します。INSERT、UPDATE、DELETEなどの発行時に使用します。

$q = 'INSERT INTO acms_entry (col1, col2, col3....) VALUES(val1, val2, val3...);'
$res = $DB->query($q, 'exec');

if ( $res == true ) {
   // success
} else {
   // fail
}

データベース関係クラスの使用(2) SQL Select編

SQLクラス

SQLクラスは、a-blog cmsで使用しているSQL文の作成を支援するクラスです。DBクラスとあわせて使用します。今回はSelect文を発行するケースで順にサンプルを紹介します。

基本のSELECTからテンプレートへの追加まで

// Templateクラス
$Tpl = new Template($this->tpl, new ACMS_Corrector());

// DBクラス @dsn()はconfig.server.php で定義されたデータベース情報を返します
$DB = DB::singleton(dsn());

// SQLクラス
$SQL = SQL::newSelect('entry');

// 対象の列を設定
$SQL->addSelect('entry_title');
$SQL->addSelect('entry_datetime');

// SQLを組み立てる
$q = $SQL->get(dsn());

// クエリーを実行して結果を取得
$all = $DB->query($q, 'all');

// 取得したデータをentry:loopとしてテンプレートに追加
foreach ( $all as $row ) {
    $Tpl->add('entry:loop', $row);
}

return $Tpl->get();

日付が新しい順に並び替えて10件取得する

$SQL =  SQL::newSelect('entry');

$SQL->addSelect('entry_title');
$SQL->addSelect('entry_datetime');

// エントリーの日付で降順に並び替える
$SQL->setOrder('entry_datetime', 'DESC');

// 取得数を10までに制限する
$SQL->setLimit(10);

$q = $SQL->get(dsn());
$all = $DB->query($q, 'all');

?>

カテゴリーごとにエントリーの数をカウントする

$SQL = SQL::newSelect('entry');

// 合計をカウントする ( MAX・MINなど集計関数 )
$SQL->addSelect('*', 'amount', null, 'COUNT');

// カテゴリーIDでグループする
$SQL->setGroup('entry_category_id');

$q = $SQL->get(dsn());
$all = $DB->query($q, 'all');

WHEREの処理

entry_blog_idが1のエントリー

$SQL = SQL::newSelect('entry');
$SQL->setSelect('*');


// 第三引数以降は省略できます
$SQL->addWhereOpr('entry_blog_id', 1, '=', 'AND');

$q = $SQL->get(dsn());
$all = $DB->query($q, 'all');

entry_category_idが1 または entry_user_idが1のエントリー

$SQL = SQL::newSelect('entry');
$SQL->setSelect('*');

// entory_category_idが1
$SQL->addWhereOpr('entry_category_id', 1);

// または entry_user_idが1
$SQL->addWhereOpr('entry_user_id', 1, '=', 'OR');

$q = $SQL->get(dsn());
$all = $DB->query($q, 'all');

entry_statusがdraft(下書き) か close(非公開)であるエントリー

$SQL = SQL::newSelect('entry');
$SQL->setSelect('*');

// draft か close である ( IN )
$SQL->addWhereIn('entry_status_id', array('draft', 'close'));

$q = $SQL->get(dsn());
$all = $DB->query($q, 'all');

title, body, nameのいずれかに指定した文字列を含むコメント

$keyword   = 'hogehoge';

$SQL = SQL::newSelect('comment');
$SQL->addSelect('*');

$SQL->addWhereOpr('CONCAT(comment_title, comment_body, comment_name)', "%$keyword%", 'LIKE');

$q = $SQL->get(dsn());
$all = $DB->query($q, 'all');

バージョンアップ時に上書きされないユーザー定義php (config.user.php)について

サーバーのドメインやデータベースの情報などはconfig.server.phpで設定します。ただし、このファイルは自動アップデートを利用すると上書き保存されてしまうため、最初から記述されている設定以外を定義しておくことができません。

そこで、Ver.2.10より config.user.php を用意しました。このファイルに記述されている内容は config.server.phpがロードされるタイミングと同じタイミングでロードされ、CMSのアップデートによって上書きされません。 CMS起動時に処理したい内容はここに記述しておきましょう。