Twig でプラグイン(プラグイン(拡張アプリ))の管理画面を実装する
プラグイン(拡張アプリ)の管理画面を Twig テンプレートで組み立てられます。本ドキュメントでは、仕組みの全体像から、最小構成の画面を表示する手順、設定値の保存・バリデーションを伴う設定画面の作り方までを通して解説します。
これまで管理画面への差し込みは 標準テンプレートエンジンが中心でしたが、Twig で部分的に描画して差し込めるようになりました。テンプレートの継承(extends)やマクロといった Twig の表現力を、管理画面の拡張でもそのまま使えます。
対応バージョン: Ver. 3.2.4 以上で利用できます。
2 つの Twig 経路を区別する
a-blog cms には Twig を使う経路が 2 つあり、本ドキュメントで扱うのは後者です。両者は独立していて、片方を使うのにもう片方の設定は要りません。
経路 | 有効化の方法 | 主な用途 |
|---|---|---|
公開側テーマ全体の Twig 化 | コンフィグ | 公開ページのテンプレートを |
プラグイン(拡張アプリ)からの部分描画 | フック・ | 管理画面に独自の UI / 設定画面を差し込む |
tpl_twig を有効にしていなくても、プラグイン側の経路は使えます。
全体像
プラグインの ServiceProvider::init() で各種登録を行い、管理画面のレンダリング時に差し込みポイントへ HTML を流し込みます。
ServiceProvider::init()
├─ Twig::addTemplatePath() … 自前テンプレートディレクトリを名前空間付きで登録
├─ InjectTemplate::add($type, …) … 差し込みポイントに「文字列パス」または「callable」を登録
└─ (Filter / Function / Extension は extendsTwig フックで登録)
│ 管理画面リクエスト
▼
Admin_InjectTemplate モジュール(差し込みポイント)
└─ 登録された callable を実行
└─ Twig::renderTemplate('@namespace/...') の結果 HTML を差し込む差し込みポイントは、システムテーマ側のテンプレートに置かれた Admin_InjectTemplate モジュールです。プラグインはそこに対して「この $type のときはこの HTML を出す」と登録します。
実装は次の API を組み合わせます。詳細は各リファレンスを参照してください。
Twig 拡張 API — テンプレートディレクトリの登録(
Twig::addTemplatePath)、部分描画(Twig::renderTemplate)、独自 Filter / Function / Extension の登録(extendsTwigフック)。管理画面への差し込み(InjectTemplate) —
InjectTemplate::add()と、利用できる差し込みポイント($type)の一覧。組み込み Twig 関数 acms_config() — Twig からコンフィグ値を参照する。閲覧側テーマでも使えます。
同梱の SamplePlugin(extension/plugins/SamplePlugin/)が、本機能の動作するサンプルです。合わせて参照してください。
チュートリアル: 最小構成の管理画面
まずは、プラグインの管理メニューを開いたときに Twig で書いた画面を表示するところまでを作ります。
1. テンプレートを用意する
プラグイン内にテンプレートディレクトリを作り、Twig ファイルを置きます。
extension/plugins/MyPlugin/
├─ ServiceProvider.php
└─ template/
└─ admin/
└─ main.twig差し込みポイントには、管理画面シェル(ヘッダー・メニューなど)の内側に HTML が挿入されます。そのため main.twig にはページ全体ではなく、表示したい中身の断片だけを書きます。
<header>
<h1 class="acms-admin-admin-title">MyPlugin 設定</h1>
</header>
<p>はじめての Twig 管理画面です。</p>2. テンプレートディレクトリを登録する
ServiceProvider::init() で、テンプレートディレクトリを名前空間付きで登録します。Twig::addTemplatePath() に渡すのはファイルシステムのパスなので PLUGIN_LIB_DIR を使います。
use Acms\Services\Facades\Twig;
Twig::addTemplatePath(PLUGIN_LIB_DIR . 'MyPlugin/template', 'myplugin');これで、テンプレートを @myplugin/admin/main.twig のように名前空間付きで参照できます。
3. 差し込みポイントに登録する
管理画面のメイン領域には admin-main という差し込みポイントがあります。ここに「HTML を返す callable」を登録すると、その戻り値が画面に差し込まれます。callable の中で Twig::renderTemplate() を呼んで、Twig の描画結果を返します。
use Acms\Services\Common\InjectTemplate;
use Acms\Services\Facades\Twig;
$inject = InjectTemplate::singleton();
if (defined('ADMIN') && ADMIN === 'app_' . $this->menu) {
$inject->add('admin-main', static function (): string {
return Twig::renderTemplate('@myplugin/admin/main.twig');
});
}ADMIN === 'app_' . $this->menu で「自分のプラグイン画面を開いているときだけ」差し込むようにしています。$this->menu は ServiceProvider の $menu プロパティで指定したメニュー識別子です。
4. 完成形(ServiceProvider::init)
ここまでをまとめると、init() は次のようになります。
use Acms\Services\Common\InjectTemplate;
use Acms\Services\Facades\Twig;
public function init()
{
// 1. テンプレートディレクトリの登録(init で一度だけでよい)
Twig::addTemplatePath(PLUGIN_LIB_DIR . 'MyPlugin/template', 'myplugin');
// 2. 自分の管理メニュー画面のときだけ、メイン領域に差し込む
if (defined('ADMIN') && ADMIN === 'app_' . $this->menu) {
$inject = InjectTemplate::singleton();
$inject->add('admin-main', static function (): string {
return Twig::renderTemplate('@myplugin/admin/main.twig');
});
}
}5. 動作確認
プラグインを有効化し、管理画面のプラグインメニューから対象の画面を開きます。main.twig の内容が表示されれば成功です。
設定画面を Twig で書く
acms_config() と a-blog cms 標準のフォームを組み合わせると、コンフィグの保存・バリデーションを伴う設定画面を Twig だけで書くことができます。
値を参照するだけの基本 API は 組み込み Twig 関数 acms_config() を参照してください。ここでは、フォーム送信・保存・バリデーションの「編集レイヤー」を扱います。
保存の仕組み
設定画面のフォームは、標準の ACMS_POST_Config に送信します。
送信ボタンの
nameをACMS_POST_Configにする<input name="config[]" value="保存するキー">で保存対象のキーを宣言するバリデーションは hidden input で宣言する(後述)
ACMS_POST_Config が POST 値を保存し、バリデーション結果を保持したまま画面へ戻します。Twig 側は acms_config() から保存通知やエラーを取得して表示します。
<form action="" method="post" class="acms-admin-form">
<button type="submit" name="ACMS_POST_Config" class="acms-admin-btn-admin acms-admin-btn-admin-primary">
保存
</button>
<input type="text" name="site_title" value="{{ config.get('site_title') }}">
<input type="hidden" name="config[]" value="site_title">
</form>config[] で宣言したキーだけが保存対象になります。
バリデーションの宣言
バリデーションは a-blog cms 標準フォームと同じ作法で、hidden input として宣言します。ACMS_POST_Config がこれを解釈し、自動的に検証します。
<input type="hidden" name="キー:validator#メソッド" value="引数">value属性がバリデータへの引数になります(例:maxlengthの文字数)。:validator#は短縮形の:v#でも書けます。
<!-- 必須・最大 50 文字 -->
<input type="hidden" name="site_title:validator#required" value="true">
<input type="hidden" name="site_title:validator#maxlength" value="50">使える主なバリデータ
メソッド | 引数 | 内容 |
|---|---|---|
| — | 必須 |
| — | メールアドレス形式 |
| 文字数 | 最大文字数 |
| — | 数字のみ |
| 正規表現 | パターンに一致 |
| 候補(カンマ区切り等) | 許可値のいずれか |
| 数値 | 最小値 / 最大値 |
| 比較対象 | 値が一致 |
詳しくは フォームオプション をご覧ください。
結果を取得するメソッド
acms_config() のアクセサから、保存通知とバリデーション結果を取得できます。
メソッド | 戻り値 | 説明 |
|---|---|---|
| bool | 設定が保存されたかどうか |
| string | 保存通知の文字列( |
| bool | 直近のリクエストがコンフィグ保存のPOSTりくえすとだったかどうか |
| bool | フォーム全体がバリデーションを通過したか |
| array | 失敗したフィールドの一覧( |
| bool | 指定フィールド(とメソッド)が失敗したか |
引数のないメソッドはプロパティ記法でも書けます(config.saved / config.valid / config.errors)。
保存通知とエラーサマリー
{% if config.saved %}
<div role="alert" class="acms-admin-alert acms-admin-alert-info">保存しました</div>
{% endif %}
{% if config.posted and not config.valid %}
<div role="alert" class="acms-admin-alert acms-admin-alert-danger">
入力内容に誤りがあります
{% if config.errors %}
<ul>
{% for field, methods in config.errors %}
<li>{{ field }}: {{ methods|join(', ') }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endif %}フィールドごとのエラー表示
invalid() で、特定フィールドの特定バリデータが失敗したかを判定できます。標準フォームと同じ data-validator-label のパターンに合わせると、既存の管理画面の見た目に揃います。
<input type="text" name="site_title" value="{{ config.get('site_title') }}">
<input type="hidden" name="config[]" value="site_title">
<input type="hidden" name="site_title:validator#required" value="true" id="validator-site_title-required">
<div role="alert" aria-live="assertive">
<div data-validator-label="validator-site_title-required"
class="validator-result-{{ config.invalid('site_title', 'required') ? '0' : '1' }}">
<p class="error-text">サイトタイトルを入力してください</p>
</div>
</div>バリデーションエラーで再描画されたときも、acms_config() は POST 値を重ね合わせるため config.get() がユーザーの入力値を返します。入力をやり直させずに済みます。
完成例
テキスト(必須・最大文字数)、メール(必須・形式)、セレクト、チェックボックスを含む設定画面の例です。同梱の SamplePlugin(extension/plugins/SamplePlugin/template/admin/main.twig)が動作するサンプルです。
{% set config = acms_config(blogId: BID) %}
{% if config.saved %}
<div role="alert" class="acms-admin-alert acms-admin-alert-info">設定を保存しました</div>
{% endif %}
{% if config.posted and not config.valid %}
<div role="alert" class="acms-admin-alert acms-admin-alert-danger">入力内容に誤りがあります</div>
{% endif %}
<form action="" method="post" class="acms-admin-form">
<button type="submit" name="ACMS_POST_Config" class="acms-admin-btn-admin acms-admin-btn-admin-primary">
保存
</button>
{# テキスト:必須・最大 50 文字 #}
<label for="sample_config_text">サンプルテキスト</label>
<input id="sample_config_text" type="text" name="sample_config_text" value="{{ config.get('sample_config_text') }}">
<input type="hidden" name="config[]" value="sample_config_text">
<input type="hidden" name="sample_config_text:validator#required" value="true" id="validator-sample_config_text-required">
<input type="hidden" name="sample_config_text:validator#maxlength" value="50" id="validator-sample_config_text-maxlength">
<div role="alert" aria-live="assertive">
<div data-validator-label="validator-sample_config_text-required"
class="validator-result-{{ config.invalid('sample_config_text', 'required') ? '0' : '1' }}">
<p class="error-text">サンプルテキストを入力してください</p>
</div>
<div data-validator-label="validator-sample_config_text-maxlength"
class="validator-result-{{ config.invalid('sample_config_text', 'maxlength') ? '0' : '1' }}">
<p class="error-text">50 文字以内で入力してください</p>
</div>
</div>
{# メール:必須・メール形式 #}
<label for="sample_config_email">メールアドレス</label>
<input id="sample_config_email" type="text" name="sample_config_email" value="{{ config.get('sample_config_email') }}">
<input type="hidden" name="config[]" value="sample_config_email">
<input type="hidden" name="sample_config_email:validator#required" value="true" id="validator-sample_config_email-required">
<input type="hidden" name="sample_config_email:validator#email" value="true" id="validator-sample_config_email-email">
{# セレクト #}
<label for="sample_config_status">公開ステータス</label>
<select id="sample_config_status" name="sample_config_status">
<option value="open" {{ config.selected('sample_config_status', 'open') }}>公開</option>
<option value="close" {{ config.selected('sample_config_status', 'close') }}>非公開</option>
</select>
<input type="hidden" name="config[]" value="sample_config_status">
{# チェックボックス:未チェック時に off を送るため hidden を前置 #}
<label>メールマガジン</label>
<input type="hidden" name="sample_config_mail" value="off">
<input type="checkbox" name="sample_config_mail" value="on" {{ config.checked('sample_config_mail', 'on') }} id="sample_config_mail">
<input type="hidden" name="config[]" value="sample_config_mail">
</form>
つまずきやすいポイント
テンプレートが見つからない:
@myplugin/...の名前空間がaddTemplatePath()の第 2 引数と一致しているか、ファイルシステムのパス(PLUGIN_LIB_DIR)を渡しているかを確認してください。画面に何も出ない:
ADMINの比較条件を確認してください。$menuを設定していない、または条件が現在の画面と一致していない可能性があります。ページ全体を書いてしまう: 差し込みポイントには管理画面テンプレートの内側に断片が挿入されます。
<html>や共通メニューは書かず、中身だけを出力してください。@systemを自分のテンプレートに使ってしまう:@systemはシステムテーマ参照用の予約名前空間です。自前のテンプレートには別の名前空間(例ではmyplugin)を使ってください。保存されない: 保存したいキーを
<input name="config[]" value="キー">で宣言しているか確認してください。宣言のないキーは保存されません。チェックボックスの OFF が保存されない: チェックボックスは未チェック時に値が送信されません。OFF を保存したい場合は、同名の hidden(
value="off")をチェックボックスの前に置きます。