🌐 言語: 🇻🇳 Tiếng Việt · 🇬🇧 English · 🇯🇵 日本語(現在)
安全に更新できるプラグイン — ルール、独自データテーブル、update() フック
はじめに
このドキュメントは、新しいリリースのたびに他の人のサイトで 安全に更新できる プラグインを作りたい GP247 プラグイン開発者向けです。
設定が消えない、データが壊れない、アンインストールして再インストールしてもテーブルがすべて揃う、という状態を目指します。
更新がどう動くか、守るべき 6 つのルール、独自データテーブルの作り方、update() フックの書き方を説明します。プラグイン作成ガイド の後に読むことをおすすめします。
💡 AI エージェントと一緒に作業しますか? gp247-skills リポジトリにある拡張機能作成スキル —
gp247-plugin-create、gp247-payment-plugin-create、gp247-shipping-plugin-create、gp247-template-create— は、どれも以下の安全な更新ルールに沿ってプラグイン/テンプレートを作ります。 コマンドラインでの拡張機能のインストール/更新/アンインストールには、スキルgp247-extension-lifecycleを使います。
更新の仕組み
新しいバージョンをサイトに届ける経路は 2 つあります。
flowchart LR
A["ワンクリック更新<br/>バックアップ → ダウンロード → 検証 → 上書き"] --> U["update($fromVersion)"]
B["ファイルを手動で置き換え<br/>その後 gp247:update / 「データ更新を適用」ボタン"] --> U
U -. "エラー(ワンクリック)" .-> R["旧バージョンを復元"]
- ワンクリックの経路:上書きの前に、システムが checksum、
configKey、version、requireUpdateFrom、requireCoreを確認します。 上書きの段階またはupdate()でエラーが起きると、プラグインはバックアップから 復元 されます。 - ファイルを手動で置き換える経路(
git pull、Composer、手作業のコピー。その後gp247:update、gp247:ext-update --local、または 「データ更新を適用」(Apply data update) ボタン):ファイルのバックアップはありません。update()でエラーが起きた場合、プラグインは 「適用待ち」の状態のまま残り、再実行できます。GP247 がバージョンを記録する前にインストールされたサイトでは、update()が$fromVersion = nullで 1 回 呼ばれます。
| 場所 | 更新時 |
|---|---|
app/GP247/Plugins/<Name>/…(コード、config.php、ビュー) |
新しいバージョンで 削除して置き換え |
public/GP247/Plugins/<Name>/…(プラグインの css、js、画像) |
削除して置き換え |
admin_config の行(有効/無効、設定) |
そのまま保持 |
| プラグイン独自のデータテーブル、メニュー | そのまま保持 — 新しいバージョンは update() で追加 |
6 つのルール
| # | ルール | 理由 |
|---|---|---|
| 1 | リリースごとに version を上げる。configKey は変えない |
システムはこの 2 つの値で新旧のバージョンを照合する |
| 2 | ストアオーナーが変更する設定はデータベースに置く | 更新はすべてのファイルを上書きする |
| 3 | update() は再実行しても安全で、null を処理できる |
git pull の後にも、何度も実行されることがある |
| 4 | ユーザーがアップロードしたファイルをプラグインのフォルダーに保存しない | 更新時にフォルダーが削除され置き換えられる |
| 5 | requireCore と requireUpdateFrom を正しく設定する |
未検証のコアへのインストールや、古すぎるバージョンからの更新を防ぐ |
| 6 | 独自データテーブルは再インストール時に作り直せる | アンインストール → 再インストールでテーブルがすべて揃う必要がある |
ルール 1 — version を上げ、configKey は変えない
- リリースのたびに
gp247.jsonのversionを上げます(1.0→1.1)。インストール済みのバージョンより大きくないと、ワンクリック更新は 拒否されます("not newer")。 - 公開した後は
configKeyを絶対に変えないでください。変えると、システムは別のプラグインとして扱います。
ルール 2 — 設定はデータベースに置く
標準の方法:設定 1 つにつき 1 行 の admin_config。キーは <configKey>_<name> で、install() で作り、update() で追加し、
ConfigForm 画面で編集します — 完全なコードは プラグイン作成ガイド の手順 3〜4 を見てください。
秘密鍵(password 項目、暗号化)を保存できるのはこの方法だけです。
代わりの方法:JSON の 1 行 — 表形式の設定画面では表せない、構造を持つ設定(リスト、入れ子)向けです。
config.php に既定値を置きます。スケルトンは function.php にサンプル関数を 2 つ(コメントアウトした状態で)生成しています。
// 有効な設定 = 既定値(config.php)にオーナーの選択(admin_config)を重ねたもの
function MyPlugin_effective_config()
{
$defaults = (array) config('Plugins/MyPlugin.settings', []);
$row = \GP247\Core\Models\AdminConfig::where('group', 'Plugins')->where('key', 'MyPlugin_config')->first();
$overrides = $row ? json_decode((string) $row->value, true) : null;
return is_array($overrides) ? array_merge($defaults, $overrides) : $defaults;
}
// オーナーの選択をデータベースに保存する(更新後も残る)
function MyPlugin_save_config(array $settings)
{
\GP247\Core\Models\AdminConfig::updateOrCreate(
['group' => 'Plugins', 'key' => 'MyPlugin_config'],
['code' => 'MyPlugin_config', 'store_id' => GP247_STORE_ID_GLOBAL, 'value' => json_encode($settings)]
);
}
JSON の行では 秘密鍵を保持できません(項目ごとの暗号化がありません)。残りの設定に JSON を使う場合でも、秘密鍵は常に
password型の独立した行に置いてください。
ルール 3 — update() を正しく書く
public function update(?string $fromVersion = null)
{
try {
// 1. 新しいバージョンで追加された設定行。保存済みの値には触れない。
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);
}
}
// 2. 1.1 で追加したカラム — 現在の状態で判定するので、null でも再実行でも安全。
$table = GP247_DB_PREFIX.'my_plugin_item';
if (\Illuminate\Support\Facades\Schema::hasTable($table)
&& !\Illuminate\Support\Facades\Schema::hasColumn($table, 'sort')) {
\Illuminate\Support\Facades\Schema::table($table, function ($t) {
$t->integer('sort')->default(0);
});
}
return ['error' => 0, 'msg' => ''];
} catch (\Throwable $e) {
return ['error' => 1, 'msg' => $e->getMessage()];
}
}
- バージョン番号を比べるだけでなく、実際の状態(
hasTable、hasColumn、行がすでにあるか)を確認します。 $fromVersionがnullのときに処理を飛ばさないでください —nullは「最も古いバージョン」として扱います。version_compare($fromVersion, …)は、状態から判断できないデータ変更にだけ使います。- 移行が安全でないときは
['error' => 1, ...]を返します(または例外を投げます)— ワンクリックの経路では旧バージョンが復元されます。
ルール 4 — ユーザーのファイルをプラグインのフォルダーに保存しない
お客様がアップロードした画像やファイルは、storage/ または共通のデータ領域 public/GP247/… に保存します。プラグインの public/ フォルダーには、
リリースに含まれるアセット(css、js、UI 用の画像)だけを置きます。
ルール 5 — requireCore と requireUpdateFrom
requireCore:各値"X.Y"は「X.x 系の X.Y 以降」という範囲です。php artisan gp247:infoに合わせて書きます。複数のメジャー系に対応する場合は 系ごとに 1 つずつ値を書きます(["3.1", "4.0"])。パッチ番号は書きません。requireUpdateFrom:直接更新できる、インストール済みの最も低いバージョンです。"1.0"は制限なしです。古い系からupdate()で移行できない場合に 引き上げます(例:"2.0")。
ルール 6 — 独自データテーブル
推奨する方法: Models/ExtensionModel.php の中で Schema を使ってテーブルを作成/削除します。この方法は 再実行できます。
installExtension() はインストールのたびに実行されるので、アンインストール → 再インストールでも必ずテーブルが作り直されます。
public function installExtension()
{
$table = GP247_DB_PREFIX.'my_plugin_item';
if (!\Illuminate\Support\Facades\Schema::hasTable($table)) {
\Illuminate\Support\Facades\Schema::create($table, function ($t) {
$t->increments('id');
$t->string('title');
$t->tinyInteger('status')->default(1);
$t->timestamps();
});
}
}
public function uninstallExtension()
{
\Illuminate\Support\Facades\Schema::dropIfExists(GP247_DB_PREFIX.'my_plugin_item');
}
install() で (new ExtensionModel)->installExtension() を、uninstall() で uninstallExtension() を呼びます。
⚠️ 注意: Laravel のマイグレーションファイルを使う場合は、
migrations台帳を片付ける必要があります。 Laravel は各マイグレーションを共通のmigrationsテーブルに記録し、 二度と実行しません。uninstall()がテーブルを削除しても台帳の行が残ると、次のインストールで "Nothing to migrate" と表示され、テーブルは 作り直されません。そのテーブルを読む画面はすべてSQLSTATE[42S02] … table doesn't existエラーになります。マイグレーションを使うなら:
アンインストール時(および
install()/update()のmigrateの前)に、そのプラグイン自身の 台帳の行をファイル名で照合して削除します。$names = array_map(fn ($p) => pathinfo($p, PATHINFO_FILENAME), glob(base_path('app/GP247/Plugins/MyPlugin/DB/migrations/*.php')) ?: []); if ($names) { \Illuminate\Support\Facades\DB::table('migrations')->whereIn('migration', $names)->delete(); }再実行しても安全なように、すべての
up()をSchema::hasTable()で囲みます。
ストアの帳簿(注文、お金、会計)であるデータは、プラグインのデータではありません — アンインストール時に削除しないでください。
リリース前に更新を試す
-
テスト用サイトに旧バージョンをインストールし、いくつか設定を変更します。
-
新しいバージョンのファイルを上書きでコピーし(ストアオーナーが
git pullするのと同じ)、次を実行します。php artisan gp247:ext-update --type=plugin --key=MyPlugin --local --dry-run php artisan gp247:ext-update --type=plugin --key=MyPlugin --local期待する結果:1 回目は適用待ちのプラグインを一覧表示し、2 回目は
update()を実行して成功を報告します。 -
古い設定がそのまま残り、新しい設定/カラムが追加されていることを確認します。コマンドをもう一度実行します — エラーが出ず、何も変わらないはずです。
条件と制約(操作の前に確認)
- 新しいバージョンは
versionが大きく、インストール済みのバージョンがrequireUpdateFrom以上である必要があります — そうでないと、ワンクリック更新は拒否されます。 - 開発中のプラグインをアンインストールするときは、必ず
--only-dataを使います — 通常のアンインストールはソースフォルダー(app/…とpublic/…)まで削除します。 - 手動でファイルを置き換える経路で
update()が失敗しても、ファイルは復元されません — 再実行のために適用待ちの状態が残るだけです。update()は 何度実行しても安全になるように書いてください。
よくある質問(Q&A)
Q1: ストアオーナーが git pull で更新した場合も、update() は実行されますか?
→ はい。php artisan gp247:update、gp247:ext-update --local を実行するか、「プラグイン」画面で「データ更新を適用」を押したときに実行されます。
Q2: なぜ update() は null を受け取るのですか?
→ GP247 がバージョンを記録する前にインストールされたサイトでは、旧バージョンが分かりません。null を最も古いバージョンとして扱い、実際の状態に基づいて処理してください。
Q3: ワンクリック更新が途中で失敗したらどうなりますか?
→ ファイルを上書きする前に作ったバックアップから、プラグインが復元されます。
Q4: アンインストールして再インストールしたら、プラグインのテーブルがなくなったのはなぜですか?
→ migrations 台帳を片付けずにマイグレーションファイルを使っています。ExtensionModel の Schema に切り替えるか、ルール 6 のとおりに台帳を片付けてください。
Q5: 新しいバージョンで設定を 1 つ追加するには、どうすればよいですか?
→ 設定の一覧にキーを追加します。update() が足りない行を作り、ほかの行の古い値はそのまま保持します。
Q6: update() の中で version_compare を使うべきですか?
→ 状態から判断できない処理(例:ある値の形式を変える)にだけ使います。カラムや行の追加は、状態を確認してください。
Q7: ユーザーがアップロードした画像ファイルが、更新後に消えたのはなぜですか?
→ プラグインのフォルダーに置かれていたため、更新時に削除され置き換えられました。storage/ または共通の public/GP247/… に保存してください。
Q8: requireUpdateFrom はいつ引き上げるのですか?
→ 新しいバージョンが古い系から移行できないときです — その系のユーザーは、直接更新する代わりに再インストールする必要があります。
📅 最終更新: 2026-10-22 · ✍️ 作成者 (Author): GP247