> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# パッケージのマイグレーション公開と更新

> Laravel 13の実装から、publishesMigrationsとloadMigrationsFromの違い、公開時のタイムスタンプ変更、再公開のリスク、既存利用者へのスキーマ変更の届け方を解説します。

DBを使うパッケージを継続して保守するには、初回インストールだけでなく、すでにテーブルが存在する利用者へ変更を届ける手順が必要です。マイグレーションの公開、実行、実行履歴は別の処理として設計してください。

このページでは[パッケージ開発の基礎](/jp/advanced/package-development)を前提に、Laravel 13の `ServiceProvider`、`VendorPublishCommand`、`Migrator` を読み解きます。フレームワークの実装は `v13.34.0` を参照しています。

## コピーして渡すか、パッケージから読み込むか

| 方法 | プロバイダーの処理 | 利用者の操作 | ファイルの管理場所 |
| - | - | - | - |
| 公開する | `publishesMigrations()` | `vendor:publish` 後に `migrate` | アプリケーションの `database/migrations` |
| 直接読み込む | `loadMigrationsFrom()` | `migrate` | インストールされたパッケージ内 |

`publishesMigrations()` は、コピー元とコピー先を公開対象として登録するだけです。プロバイダーを起動しても、ファイルのコピーやSQLの実行は行いません。

一方、`loadMigrationsFrom()` はMigratorに検索パスを登録します。通常の `migrate` でそのパスのファイルも対象になりますが、プロバイダーの起動だけでは実行されません。

```mermaid theme={null}
flowchart TD
    A["パッケージのサービスプロバイダー"] --> B["publishesMigrations()<br>コピー元とコピー先を登録"]
    B --> C["vendor:publish<br>アプリケーションへコピー"]
    C --> E["migrate<br>未実行のファイルを実行"]
    A --> D["loadMigrationsFrom()<br>Migratorの検索パスに追加"]
    D --> E
    E --> F["実行済みのファイル名を<br>migrationsテーブルに記録"]
```

利用者が実行前にテーブル名やカラムを調整する設計なら、公開方式が候補になります。パッケージがスキーマを管理し、利用者によるファイル編集を前提としないなら、直接読み込み方式も検討できます。以下の2つのプロバイダー例は代替案です。

<Warning>
  同じマイグレーションを公開しつつ直接読み込ませる設計は避けてください。公開時にタイムスタンプが変わると、コピー元とコピー先は別の実行履歴として扱われ、同じテーブル作成処理が二重に実行されるおそれがあります。
</Warning>

## 公開方式を実装する

パッケージ固有のタグを付け、利用者が他のリソースと区別して公開できるようにします。

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->publishesMigrations([
            __DIR__.'/../database/migrations' => database_path('migrations'),
        ], 'courier-migrations');
    }
}
```

初回インストールでは、対象プロバイダーとタグを指定してコピーし、内容を確認してから実行します。両方を指定した場合、Laravelはそのプロバイダーに属する、そのタグの公開対象を選びます。

```bash theme={null}
php artisan vendor:publish --provider="Acme\Courier\CourierServiceProvider" --tag=courier-migrations
php artisan migrate:status
php artisan migrate
```

### タイムスタンプの変更には設定が関係する

公式ドキュメントは、公開時にマイグレーションのタイムスタンプを現在日時へ更新する動作を説明しています。ただし、`ServiceProvider::publishesMigrations()` の実装では、`database.migrations.update_date_on_publish` が有効な場合にだけ、コピー元をタイムスタンプ更新対象へ追加します。この設定を取得するときのフォールバックは `false` です。

Laravel 13の標準アプリケーションの `config/database.php` には、次の設定があります。旧構成を引き継いだアプリケーションでは、設定が存在するかも確認してください。

```php theme={null}
'migrations' => [
    'table' => 'migrations',
    'update_date_on_publish' => true,
],
```

さらに `VendorPublishCommand` は、登録されたコピー元の実パスと一致し、コピー先の名前に `YYYY_MM_DD_HHMMSS_` 形式がある場合に日時を書き換えます。コマンド開始時刻を基準に、対象ファイルごとに1秒加算します。名前にこの形式がなければ、その処理では日時を付け足しません。

```text theme={null}
コピー元:
2026_09_01_000000_create_courier_deliveries_table.php

公開後の例:
2026_10_02_120001_create_courier_deliveries_table.php
```

上の公開後の日時は説明用です。実際のファイル名は公開した時刻によって変わります。

<Info>
  タイムスタンプ更新は、パッケージの登録と利用アプリケーションの設定の両方に依存します。パッケージのプロバイダーからこの設定を一律に変更せず、インストール手順に前提を記載してください。設定キャッシュを利用している場合は、設定変更後の再構築も必要です。
</Info>

## 再公開は「未実行のものだけ追加」ではない

`vendor:publish` はDBの実行履歴を確認しません。また、`v13.34.0` のコピー処理では、既存ファイルの確認を**タイムスタンプの変更前**のコピー先に対して行います。ディレクトリ公開でも、まずコピー元と同じ相対パスがコピー先にあるかを確認し、その後で日時を書き換えます。

そのため、最初の公開で日時が変わり、コピー元と同じ名前のファイルがアプリケーション側にない場合は、同じタグをもう一度公開すると別の日時のファイルが追加されることがあります。`--force` を付けなければ常に重複を防げる、とは考えないでください。

| 条件 | 再公開時に注意すること |
| - | - |
| 日時更新が有効で、変更前のコピー先が存在しない | 別の日時のコピーが追加され得る |
| 日時更新が無効で、コピー先が同名 | 通常は既存ファイルをスキップする |
| `--force` を指定 | コピー条件を満たし、日時更新が有効なら別名になる場合もある |
| `--existing` を指定 | 変更前のコピー先の存在を確認するため、日時変更済みファイルだけがあっても対象になるとは限らない |

### 実行済みかどうかはファイル名で判断する

`Migrator::getMigrationName()` は、ファイルのベース名から `.php` を除いた文字列を返します。未実行の判定では、この名前と実行履歴を比較します。PHPの内容やテーブル名が同じかどうかによる判定ではありません。

```text theme={null}
2026_10_02_120001_create_courier_deliveries_table
2026_10_03_090001_create_courier_deliveries_table
```

この2つは別のマイグレーション名です。前者が実行済みでも、後者はその履歴だけでは実行済みになりません。

<Warning>
  パッケージ更新のたびに初回インストール用の公開コマンドを無条件で再実行したり、`--force` を標準手順にしたりしないでください。公開済みファイルの編集を上書きするだけでなく、日時変更によって重複した処理を追加する可能性があります。
</Warning>

## 直接読み込み方式を実装する

パッケージ内のマイグレーションをそのまま実行対象にしたい場合は、検索パスを登録します。この方式では、同じファイルを公開する処理は追加しません。

```php theme={null}
<?php

namespace Acme\Courier;

use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
    }
}
```

`loadMigrationsFrom()` はMigratorが解決されたときに `path()` を呼びます。`Migrator::path()` は検索パスの重複を除き、`getMigrationFiles()` は見つけたファイルをマイグレーション名でキー付けして、その名前順に並べます。

利用者がパッケージを更新すると、新しいファイルは次の `migrate` の対象になります。既存ファイルの名前は変えず、新しいスキーマ変更には新しいファイルを追加します。他のパッケージとの衝突を避けるため、`create_courier_deliveries_table` のように機能名も含めてください。同名のファイルは同じキーになり、両方が独立して実行されるわけではありません。

<Warning>
  公開方式から直接読み込み方式への変更は、単なるプロバイダーの書き換えではありません。利用者の実行履歴が公開時の名前で記録されている場合、パッケージ側の元の名前と一致しません。既存利用者の履歴・公開済みファイル・ロールバックを含む移行手順が必要です。
</Warning>

## スキーマ変更を既存利用者へ届ける

たとえば配送テーブルへ追跡番号を追加する場合は、公開済みの `create_courier_deliveries_table` を編集するのではなく、変更用の新しいファイルを追加します。既存の作成マイグレーションを編集しても、実行済みの利用者にはその変更が実行されません。

```php database/migrations/2026_10_02_000000_add_tracking_code_to_courier_deliveries_table.php theme={null}
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('courier_deliveries', function (Blueprint $table) {
            $table->string('tracking_code')->nullable();
        });
    }

    public function down(): void
    {
        Schema::table('courier_deliveries', function (Blueprint $table) {
            $table->dropColumn('tracking_code');
        });
    }
};
```

既存行があるテーブルへ追加する例なので、ここではnullableにしています。必須化やデータの埋め戻しが必要なら、別途その手順と実行順序を設計します。

公開方式では、利用者の公開済みファイルと照合し、**今回追加したファイルだけ**を届ける更新手順を用意します。新規ファイル専用の公開タグを設ける方法もありますが、日時更新が有効ならそのタグの繰り返し実行にも同じ注意が必要です。初回用タグを再実行するだけの更新手順にはしません。

直接読み込み方式では、更新後のコードで新しいファイルを検出できます。どちらの方式でも、ファイルが存在するだけではDBは変わらないため、リリースノートにマイグレーション実行の必要性を明記します。

## リリース前に確認すること

パッケージのDBテストに加えて、利用アプリケーションで公開と更新の手順を確認してください。テストでマイグレーションを直接読み込むだけでは、ファイル名が変わる公開方式を検証したことにはなりません。

* 空のDBに初回インストールし、必要なテーブルを作成できる。
* 旧リリースのDBと実行履歴から更新し、新しい変更だけが適用される。
* 同じ公開コマンドを繰り返したときのファイル一覧を確認し、更新手順が重複を生まない。
* 日時更新の有効・無効と、公開済みファイルの編集を考慮した手順になっている。
* 新規マイグレーションのロールバックと、アプリケーションの他のマイグレーションとの実行順序を確認する。

## 関連ページ

<Columns cols={2}>
  <Card title="マイグレーション" icon="database" href="/jp/migrations">
    スキーマ定義、実行履歴、ロールバックの基本を確認します。
  </Card>

  <Card title="パッケージのテスト" icon="flask" href="/jp/advanced/package-testing">
    パッケージのサービスプロバイダーとDBをテストします。
  </Card>

  <Card title="パッケージ設定のマージとキャッシュ" icon="sliders" href="/jp/advanced/package-config-merging">
    利用者の設定と設定キャッシュを考慮した更新手順を確認します。
  </Card>

  <Card title="バージョン互換性管理" icon="code-branch" href="/jp/advanced/package-versioning">
    更新手順と互換性の変更をリリース方針に結び付けます。
  </Card>
</Columns>

## 参照した一次情報

* [Laravel公式ドキュメント: パッケージのマイグレーション](https://github.com/laravel/docs/blob/156fc7fde114548640e13c39aa79b991291c3f91/packages.md#migrations)
* [ServiceProvider: 公開対象と検索パスの登録](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Support/ServiceProvider.php)
* [VendorPublishCommand: コピーとタイムスタンプ変更](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Foundation/Console/VendorPublishCommand.php)
* [Migrator: ファイルの検出と未実行の判定](https://github.com/laravel/framework/blob/v13.34.0/src/Illuminate/Database/Migrations/Migrator.php)
* [Laravel 13の標準アプリケーション: DB設定](https://github.com/laravel/laravel/blob/06d016a364a37430eec9cbc52209adce3d7667ce/config/database.php)


## Related topics

- [Laravelパッケージ開発](/jp/advanced/package-development.md)
- [応用トピック](/jp/advanced/index.md)
- [Laravel Sanctum(APIトークン認証)](/jp/sanctum.md)
- [Laravel AI SDK](/jp/ai-sdk.md)
- [Laravel Pennant](/jp/pennant.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.