🌐 言語: 🇻🇳 Tiếng Việt · 🇬🇧 English · 🇯🇵 日本語(現在)
GP247 のプラグイン作成ガイド
はじめに
このドキュメントは、GP247 のプラグイン(管理画面で有効化/無効化/アンインストールできる追加機能パッケージ)を書きたい PHP/Laravel 開発者の出発点です。このページでは、設定画面を持ち、インストールでき、ストアオーナーの設定を失わずに 新しいバージョンへ更新できるプラグインを 6 つのステップで作ります。より詳しいトピックは個別のページにあり、すぐ下に一覧があります。 読み終えると、動作するプラグインができあがり、自分のプラグインの種類に応じて次に読むページがわかります。
正しいガイドを選ぶ
| 作っているもの… | 読むページ |
|---|---|
| 管理機能、表示ブロック、一般的なプラグイン | このページ |
| 決済方法(銀行振込、オンライン決済ゲートウェイ) | 決済プラグインの作成 |
| 配送方法(料金表、API を持つ配送業者) | 配送プラグインの作成 |
| クーポン/ポイント、独自のストアフロントページ、レイアウトブロック、ストアごとの設定、秘密鍵 | 高度なプラグイン |
| 独自のデータテーブル、新バージョンでのデータ移行 | 更新に安全なプラグイン |
| GP247 1.x 向けに書かれたプラグインのアップグレード | プラグインを v1 から v2 へ変換 |
| プラグインではなくストアの見た目(テンプレート) | テンプレートの作成 |
💡 ヒント: AI エージェントと一緒に作業しますか? gp247-skills リポジトリにある GP247 のスキルを使ってください(スキルのフォルダーをエージェントのスキルフォルダーにコピーします。例:Claude Code なら
.claude/skills/)。gp247-plugin-create(一般的なプラグイン)、gp247-payment-plugin-create(決済)、gp247-shipping-plugin-create(配送)があります。スキルはサイトのバージョンを自動で調べ、下記の手順どおりに作業します。
全体の流れ
flowchart LR
S1["1 · 雛形を生成<br/>gp247:make-plugin"] --> S2["2 · 宣言<br/>gp247.json"]
S2 --> S3["3 · 設定を<br/>データベースに保存"]
S3 --> S4["4 · 管理画面<br/>+ ルート"]
S4 --> S5["5 · インストールと<br/>確認"]
S5 --> S6["6 · 新バージョンの<br/>リリース"]
全体を通して覚えておくべき 3 つのこと:
- プラグインの更新はすべてのファイルを上書きし、データベースはそのまま残します。 ストアオーナーが変更するものは、すべてデータベースに置く必要があります。
- リリースのたびに
versionを上げます。configKeyは決して変えません。 vendor/gp247/*は変更しません。 プラグインは既存の接続ポイントを通して「差し込む」だけです。
準備
-
稼働中の GP247 3.x の Web サイトと、
php artisanを実行する権限(開発マシンまたはサーバーのターミナル)。 -
コアのバージョンを確認します — ステップ 2 で必要です。
php artisan gp247:info期待される結果:
3.1のようなcoreの行。
| 用語 | 意味 |
|---|---|
| プラグイン(extension) | 追加機能パッケージ。app/GP247/Plugins/<Name> に置かれます |
configKey |
プラグインの一意な識別子。フォルダー名と同じです |
admin_config |
データベースの設定テーブル — 更新で失われないように設定を保存する場所 |
| Livewire | Laravel で動的な画面を作る方法。GP247 の管理画面は Livewire + Tailwind(TailAdmin)を使い、jQuery は使いません |
ステップ 1 — プラグインの雛形を生成する
-
Web サイトのルートフォルダー(
artisanファイルがある場所)でターミナルを開き、次を実行します (MyPluginを自分の名前に置き換えます。スペースなしで、各単語の先頭を大文字にします)。php artisan gp247:make-plugin --name=MyPlugin --download=0期待される結果:
Successの行が表示され、フォルダーapp/GP247/Plugins/MyPluginが作成されます。(--download=1にすると、 配布用の.zipファイルがstorage/tmpに作成されます。) -
生成されたファイルを確認します。
| ファイル | 役割 | あなたの作業 |
|---|---|---|
gp247.json |
プラグインの宣言(名前、バージョン、互換性) | 編集 — ステップ 2 |
AppConfig.php |
インストール、アンインストール、有効化、無効化、更新 | 編集 — ステップ 3 |
Livewire/AdminLivewire.php |
管理画面 | 編集 — ステップ 4 |
Route.php、Provider.php |
ルート、言語、ビューの登録 | 編集 — ステップ 4 |
Lang/vi/lang.php、Lang/en/lang.php |
表示テキスト | 編集 — ステップ 4 |
Admin/AdminController.php、Views/Admin.blade.php |
旧方式の管理画面(後方互換) | Livewire を使い始めたら削除 |
Controllers/FrontController.php、Views/Front.blade.php、Seo.php |
公開ページとサイトマップ | 管理画面だけで使うプラグインなら削除 — 高度なプラグイン を参照 |
Models/ExtensionModel.php |
独自データテーブルの作成/削除 | 独自テーブルがあれば残す — 更新に安全なプラグイン を参照 |
config.php、function.php |
既定値、補助関数 | ストアオーナーに config.php を編集させてはいけません |
ステップ 2 — gp247.json を宣言する
{
"name": "My plugin",
"image": "images/logo.jpg",
"auth": "Your name",
"email": "you@example.com",
"link": "https://example.com",
"configGroup": "Plugins",
"configCode": "MyPlugin",
"configKey": "MyPlugin",
"version": "1.0",
"requireCore": ["3.1"],
"requireUpdateFrom": "1.0",
"requireComposerPackages": [],
"requireGp247Extensions": [],
"requireLivewire": false,
"storeScope": "global"
}
| フィールド | 意味 |
|---|---|
configKey |
フォルダー名と同じ。リリース後は変更しません。 |
configCode |
プラグインの役割:"Payment"(決済)、"Shipping"(配送)、"Promotion"(クーポン)。それ以外のプラグインは configKey と同じ値を書きます |
version |
1.0、1.1、2.0… リリースのたびに前のバージョンより大きくなければなりません |
requireCore |
動作するコアのバージョン:gp247:info の core の行を書きます。例:["3.1"] |
requireUpdateFrom |
このバージョンへ直接更新できる、インストール済みの最低バージョン。"1.0" は制限なし |
requireComposerPackages |
必須の Composer パッケージ。例:販売系プラグインなら ["gp247/shop"] |
requireGp247Extensions |
先にインストールが必要な他の GP247 プラグイン(configKey で指定) |
storeScope |
"global"(共通の設定が 1 つ)または "store"(ストアごとに設定) |
⚠️ 注意:
requireCoreはメジャーバージョンごとの範囲です。"3.1"は「3.x 系の中で 3.1 以上」という意味です。 3.1、3.2… にはインストールできますが、4.x では拒否されます。"2.1"と書くと、3.x のコアはインストールを拒否します。 複数のメジャー系列に対応する場合は、系列ごとに 1 つの値を書きます。例:["3.1", "4.0"]。"3.1.2"のようなパッチ番号は書きません。
ステップ 3 — 設定をデータベースに保存する
プラグインの更新はすべてのファイルを上書きします(config.php も含む)が、データベースはそのまま残します。
そのため、ストアオーナーが変更する設定は、それぞれ admin_config テーブルの 1 行にします。キーは <configKey>_<name> で、
インストール時に作成し、更新時に不足分を追加します。AppConfig.php 全体を置き換えます。
<?php
namespace App\GP247\Plugins\MyPlugin;
use GP247\Core\ExtensionConfigDefault;
use GP247\Core\Models\AdminConfig;
use GP247\Core\Models\AdminHome;
class AppConfig extends ExtensionConfigDefault
{
/** Settings the site owner edits: key => default. */
public const SETTINGS = [
'enabled_on_home' => '1',
'items_per_page' => '20',
];
public function __construct()
{
$config = json_decode(file_get_contents(__DIR__.'/gp247.json'), true);
$this->configGroup = $config['configGroup'];
$this->configKey = $config['configKey'];
$this->configCode = $config['configCode'];
$this->requireCore = $config['requireCore'] ?? [];
$this->requireComposerPackages = $config['requireComposerPackages'] ?? [];
$this->requireGp247Extensions = $config['requireGp247Extensions'] ?? [];
$this->appPath = $this->configGroup.'/'.$this->configKey;
$this->title = trans($this->appPath.'::lang.title');
$this->image = $this->appPath.'/'.$config['image'];
$this->version = $config['version'];
$this->auth = $config['auth'];
$this->link = $config['link'];
}
/**
* Every admin_config row this plugin owns: the on/off row + one row per setting.
*/
private function seedRows(): array
{
$rows = [[
'group' => $this->configGroup, 'code' => $this->configCode, 'key' => $this->configKey,
'sort' => 0, 'store_id' => GP247_STORE_ID_GLOBAL, 'value' => self::ON,
'security' => 0, 'detail' => $this->appPath.'::lang.title',
]];
$sort = 0;
foreach (self::SETTINGS as $key => $default) {
$rows[] = [
'group' => $this->configGroup, 'code' => $this->configKey.'_config',
'key' => $this->configKey.'_'.$key, 'sort' => ++$sort,
'store_id' => GP247_STORE_ID_GLOBAL, 'value' => $default,
'security' => 0, 'detail' => $this->appPath.'::lang.'.$key,
];
}
return $rows;
}
public function install()
{
if (AdminConfig::where('group', $this->configGroup)->where('key', $this->configKey)->exists()) {
return ['error' => 1, 'msg' => gp247_language_render('admin.extension.plugin_exist')];
}
try {
AdminConfig::insert($this->seedRows());
return ['error' => 0, 'msg' => gp247_language_render('admin.extension.install_success')];
} catch (\Throwable $e) {
return ['error' => 1, 'msg' => $e->getMessage()];
}
}
/**
* Add the rows a newer version introduced; never touch a value the site owner saved.
*/
public function update(?string $fromVersion = null)
{
try {
foreach ($this->seedRows() as $row) {
$exists = AdminConfig::where('group', $row['group'])->where('key', $row['key'])
->where('store_id', (string) $row['store_id'])->exists();
if (!$exists) {
AdminConfig::insert($row);
}
}
return ['error' => 0, 'msg' => ''];
} catch (\Throwable $e) {
return ['error' => 1, 'msg' => $e->getMessage()];
}
}
public function uninstall()
{
try {
AdminConfig::where('group', $this->configGroup)->where(function ($q) {
$q->where('key', $this->configKey)
->orWhere('key', 'like', $this->configKey.'\_%')
->orWhere('code', $this->configKey.'_config');
})->delete();
AdminHome::where('extension', $this->appPath)->delete();
return ['error' => 0, 'msg' => gp247_language_render('admin.extension.uninstall_success')];
} catch (\Throwable $e) {
return ['error' => 1, 'msg' => $e->getMessage()];
}
}
public function enable()
{
AdminConfig::where('group', $this->configGroup)->where('key', $this->configKey)->update(['value' => self::ON]);
return ['error' => 0, 'msg' => gp247_language_render('admin.extension.enable_success')];
}
public function disable()
{
AdminConfig::where('group', $this->configGroup)->where('key', $this->configKey)->update(['value' => self::OFF]);
return ['error' => 0, 'msg' => ''];
}
// The ⚙ (Config) button on the Plugins list appears only when this returns a redirect.
public function clickApp()
{
return redirect(gp247_route_admin('admin_myplugin.index'));
}
public function setupStore($storeId = null)
{
}
public function removeStore($storeId = null)
{
}
/**
* A setting for a store (falls back to the shared value).
*/
public function setting(string $key, $storeId = null): string
{
$storeId ??= gp247_plugin_store_id();
return (string) gp247_config($this->configKey.'_'.$key, $storeId, self::SETTINGS[$key] ?? '');
}
public function getInfo()
{
return [
'title' => $this->title,
'key' => $this->configKey,
'code' => $this->configCode,
'image' => $this->image,
'permission' => self::ALLOW,
'version' => $this->version,
'auth' => $this->auth,
'link' => $this->link,
'appPath' => $this->appPath,
];
}
}
| メソッド | 実行されるタイミング | 処理内容 |
|---|---|---|
install() |
ストアオーナーが「インストール」(Install) を押したとき | オン/オフの行と、設定ごとに 1 行を書き込む |
update(?string $fromVersion) |
ファイルが新バージョンに置き換えられた後(ワンクリック、git pull、Composer) |
不足している行を追加するだけ。何度実行しても安全。null を受け取ることがある |
uninstall() |
ストアオーナーが「削除」(Uninstall) を押したとき | プラグインのすべての行を削除 |
enable()/disable() |
有効化/無効化 | フラグを切り替えるだけで、データは削除しない |
clickApp() |
プラグイン一覧が ⚙ ボタンを描画するとき | 設定画面へのリダイレクトを返す — null を返すとボタンが出ない |
setting() |
あなたのコードが値を必要とするとき | ストアごとの値を読み、なければ共通の値を使う |
設定が秘密鍵(API キー、パスワード)の場合は? 'security' => 1 で行を作り、ステップ 4 でフィールドを password と宣言します。
コアが暗号化し、ブラウザーには決して送り返しません。詳細:高度なプラグイン。
ステップ 4 — 管理画面とルート
設定画面 — コアの ConfigForm を継承するだけで、レイアウト、権限、保存ボタン、ストア選択が揃います。
Livewire/AdminLivewire.php を置き換えます。
<?php
namespace App\GP247\Plugins\MyPlugin\Livewire;
use App\GP247\Plugins\MyPlugin\AppConfig;
use GP247\Core\AdminShell\Infrastructure\ConfigForm;
/**
* Settings screen: one field per row seeded by AppConfig::install().
*/
class AdminLivewire extends ConfigForm
{
protected ?string $permission = null;
protected function group(): string
{
return 'Plugins';
}
protected function heading(): string
{
return trans('Plugins/MyPlugin::lang.title');
}
// Required: without keys() the screen would list — and save — nothing of yours.
protected function keys(): array
{
return array_map(fn ($key) => 'MyPlugin_'.$key, array_keys(AppConfig::SETTINGS));
}
protected function fieldTypes(): array
{
return [
'MyPlugin_enabled_on_home' => 'bool',
'MyPlugin_items_per_page' => 'number',
];
}
}
フィールドの種類:bool(スイッチ)、number、select(fieldOptions() と併用)、password、text(既定)。
fieldHints()(フィールドの横のヒント)と sections()(見出し付きのブロックに分ける)も追加できます。
設定形式ではない独自の画面が必要なプラグインは、GP247\Core\AdminShell\Infrastructure\GP247AdminComponent を継承し、ビューを自分で書きます。
ルート — Route.php を置き換え、管理画面のパスを Livewire 画面に直接向けます。
<?php
use Illuminate\Support\Facades\Route;
$config = json_decode(file_get_contents(__DIR__.'/gp247.json'), true);
if (gp247_extension_check_active($config['configGroup'], $config['configKey'])) {
Route::group(
[
'prefix' => GP247_ADMIN_PREFIX.'/myplugin',
'middleware' => GP247_ADMIN_MIDDLEWARE,
],
function () {
Route::get('/', \App\GP247\Plugins\MyPlugin\Livewire\AdminLivewire::class)
->name('admin_myplugin.index');
}
);
}
その後、Admin/AdminController.php と Views/Admin.blade.php(旧方式の画面)を削除します。
プロバイダー — Provider.php は言語とビューを読み込み、プラグインの Livewire 名を登録します。
<?php
$config = json_decode(file_get_contents(__DIR__.'/gp247.json'), true);
$extensionPath = $config['configGroup'].'/'.$config['configKey'];
$this->loadTranslationsFrom(__DIR__.'/Lang', $extensionPath);
if (gp247_extension_check_active($config['configGroup'], $config['configKey'])) {
$this->loadViewsFrom(__DIR__.'/Views', $extensionPath);
if (class_exists(\Livewire\Livewire::class)) {
\Livewire\Livewire::addNamespace('MyPlugin', classNamespace: 'App\\GP247\\Plugins\\MyPlugin\\Livewire');
}
}
言語 — 表示するテキストはすべて trans('Plugins/MyPlugin::lang.<key>') を通し、vi と en の両方を用意します。
各設定フィールドのラベルは、設定名と同じキーです。
<?php
return [
'title' => 'マイプラグイン',
'enabled_on_home' => 'ホームページに表示',
'items_per_page' => '1 ページあたりの件数',
];
UI のルール:<x-gp247::*> コンポーネント、Livewire、Alpine だけを使います。jQuery、AdminLTE、select2 は使いません。
ステップ 5 — インストールと確認
-
キャッシュを削除して新しいファイルを読み込みます。
php artisan optimize:clear -
プラグインをインストールします:管理画面 → プラグイン → MyPlugin → 「インストール」(Install)、またはコマンドで実行します。
php artisan gp247:ext-install --type=plugin --key=MyPlugin期待される結果:プラグインが有効の状態で表示されます。
-
⚙ 「設定」(Config) を押し、値を 1 つ変更して 「保存」(Save) を押し、ページを再読み込みします — 値が残っていることを確認します。
-
有効化/無効化を試し、管理画面をライトモードとダークモードの両方で開きます。
-
アンインストールしてから再インストールを試します:再インストールで設定の行(テーブル、メニューがあればそれも)がすべて作成される必要があります。
⚠️ 注意: 開発中のプラグインをアンインストールするときは、必ず
--only-dataを使います:php artisan gp247:ext-uninstall --type=plugin --key=MyPlugin --only-data通常のアンインストール(または「データのみ削除」を選ばずに 「削除」(Uninstall) ボタンを押す)は、プラグインのソースフォルダーまで削除します。
ステップ 6 — 新バージョンのリリース
-
gp247.jsonのversionを上げます(例:1.0→1.1)。 -
新バージョンで設定が増えますか?
SETTINGSにキーを追加します —update()が不足している行を自動で作成し、既存の値はそのまま残します。 -
新バージョンで列/テーブルが増える、またはデータが変わりますか? 更新に安全なプラグイン に従って
update()に書きます。 -
ストアオーナーが
git pullで更新する場合と同じように試します。php artisan gp247:ext-update --type=plugin --key=MyPlugin --local --dry-run期待される結果:プラグインが適用待ちの一覧に表示されます。
--dry-runを外すとupdate()が実際に実行されます。
リリース前のチェックリスト
-
gp247.json:configKeyがフォルダー名と同じ。requireCoreがgp247:infoに従っている。versionを上げた。 - ストアオーナーが変更する設定はすべて
admin_configにある。config.phpには作者の既定値だけがある。 -
install()/uninstall()が対になっている。update()は再実行しても安全で、nullでも動作する。 -
clickApp()が設定画面を開く。keys()がすべてのフィールドを列挙している。秘密鍵はpasswordフィールドになっている。 - 管理画面は Livewire +
<x-gp247::*>を使い、jQuery を使っていない。テキストはtrans()を通し、viとenの両方がある。 -
optimize:clearを実行し、インストール/有効化/無効化/--only-dataでのアンインストール/再インストールに成功した。 - 決済/配送プラグイン:その種類専用のガイドに従った。
条件と制約(操作の前に確認)
インストール時
requireCoreが稼働中のコアの系列(メジャーバージョンごとの範囲)と一致しない場合、コアはインストールを拒否します — 未検証のコアでプラグインが動くのを防ぎます。- オン/オフの行がデータベースに残っている状態での再インストールは拒否されます(「このプラグインは既に存在します」)— 先にアンインストールしてください。
requireComposerPackagesのパッケージ、またはrequireGp247Extensionsのプラグインが不足していると、インストールできません。
更新時
- 新しい
versionはインストール済みのものより大きく、インストール済みのものがrequireUpdateFrom以上である必要があります — そうでないと、ワンクリック更新は拒否されます。 update()がエラーを返すと、旧バージョンが復元されます(ワンクリック更新)。git pullの場合はファイルは復元されず、再実行できるよう「適用待ち」の状態だけが残ります。
よくある質問(Q&A)
Q1: プラグインのファイルを 1 つずつ手書きする必要がありますか?
→ いいえ。php artisan gp247:make-plugin --name=YourPlugin --download=0 が雛形を生成します。編集するのはステップ 2~4 のファイルだけです。
AI エージェントと作業する場合は、スキル gp247-plugin-create を使います。
Q2: configKey はフォルダー名と同じでなければなりませんか?
→ はい。更新時に旧バージョンと新バージョンを照合するための識別子なので、リリース後に変更してもいけません。
Q3: ストアオーナーが config.php を編集したら、更新後にすべて消えました。なぜですか?
→ 更新はすべてのファイルを上書きするからです。設定はステップ 3 のようにデータベースに置く必要があります。
Q4: プラグインをインストールすると、コアが「互換性がない」と表示しますか?
→ requireCore が一致していません。php artisan gp247:info を実行し、core の行を正しく書きます(例:["3.1"])。古い値 "2.1" は 3.x のコアにインストールできません。
Q5: プラグイン一覧に ⚙ 「設定」ボタンが表示されませんか?
→ clickApp() が null(雛形の既定)を返しています。ステップ 3 のように、設定画面へのリダイレクトを返すようにしてください。
Q6: 設定画面にフィールドが表示されない、または保存しても保存されませんか?
→ 設定画面に keys() がないか、install() がそれらのキーの行を作成していません(--only-data でアンインストールしてから再インストールします)。
Q7: 新バージョンでデータベースの列が増えます。どこで処理しますか?
→ update() の中で、バージョン番号だけに頼らず、実際の状態(Schema::hasColumn)を確認して処理します。更新に安全なプラグイン を参照してください。
Q8: 管理画面で jQuery や select2 を使ってもよいですか?
→ いいえ。GP247 の管理画面は jQuery を読み込みません。Livewire/Alpine と <x-gp247::*> コンポーネントを使ってください。
Q9: version は 1.0.0 と 1.0 のどちらで書きますか?
→ どちらの数値形式でも構いません。各リリースが前のものより大きければ大丈夫です(version_compare で比較します)。例:1.0 → 1.1 → 2.0。
Q10: 更新したのに、管理画面に旧バージョンが表示されますか?
→ php artisan optimize:clear を実行してからページを再読み込みしてください — Laravel が古いキャッシュを保持しています。
変更履歴
| 日付 | GP247 バージョン | 変更内容 |
|---|---|---|
| 2026-10-18 | gp247/core 3.1 | 6 ステップのメインページに再構成。更新に安全なプラグイン と 高度なプラグイン を分離。決済プラグインと配送プラグインのガイドを追加。修正: requireCore を gp247:info に従って書く(古い値 "2.1" は 3.x のコアに拒否される)。既定の設定はキーごとに 1 行で保存し、ConfigForm 画面を使う(秘密鍵を保持できる)。「設定」ボタンのために clickApp() を追加 |
| 2026-09-29 | フック AppConfig::update($fromVersion) が、サイトオーナーが git pull/Composer/手動コピーでファイルを置き換えたときにも呼ばれるようになりました(gp247:update、gp247:ext-update --local、「データ更新を適用」(Apply data update) ボタン経由)。初回は null を受け取ることがあります。 |
📅 最終更新: 2026-10-22 · ✍️ 作成者 (Author): GP247