7.

Laravel マイグレーション完全ガイド — テーブル定義と運用

編集
この記事の要点
  • マイグレーションはテーブル定義のバージョン管理php artisan make:migration create_users_table でファイル生成
  • Schema::create() 内で $table->string() / foreignId() 等のカラム定義
  • php artisan migrateup() 実行、migrate:rollbackdown() 実行
  • php artisan migrate:refresh = rollback + migrate、migrate:fresh = 全 DROP + migrate(本番禁止)
  • 本番運用は migrate:status で適用状況を確認、down() も必ず書く

マイグレーションとは

マイグレーション (migration) はテーブル定義の変更を PHP コードで記述し、Git で管理する仕組みです。チーム開発で「自分の DB だけ古いカラムが残っている」事故を防げます。Laravel では database/migrations/ ディレクトリに保存されます。

マイグレーションファイル生成

# create テーブル用(推奨命名)
php artisan make:migration create_users_table

# 既存テーブル変更用
php artisan make:migration add_phone_to_users_table --table=users

# テーブル名を明示
php artisan make:migration create_articles_table --create=articles

# 生成されるファイル名
# database/migrations/2026_05_18_120000_create_users_table.php

ファイル名先頭のタイムスタンプが実行順序を決めます。一度コミットしたら基本的に変更せず、新しいマイグレーションを追加していきます。

基本のマイグレーションファイル

id();                        // bigint UNSIGNED AUTO_INCREMENT PRIMARY KEY
            $table->string('name', 100);
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();                // created_at / updated_at
        });
    }

    public function down(): void {
        Schema::dropIfExists('users');
    }
};

カラム型一覧

メソッドMySQL 型用途
$table->id()BIGINT UNSIGNED AUTO_INCREMENT主キー(推奨)
$table->bigIncrements('id')同上id() の旧表記
$table->string('name', 255)VARCHAR(255)短い文字列
$table->text('body')TEXT長文
$table->longText('content')LONGTEXT4GB まで
$table->integer('count')INT整数
$table->unsignedBigInteger('user_id')BIGINT UNSIGNEDFK 用
$table->decimal('price', 10, 2)DECIMAL(10,2)金額
$table->boolean('is_active')TINYINT(1)真偽
$table->date('birthday')DATE日付
$table->datetime('start_at')DATETIME日時
$table->timestamp('created_at')TIMESTAMPタイムスタンプ
$table->json('meta')JSONJSON カラム
$table->enum('status', ['draft','published'])ENUM列挙
$table->uuid('uuid')CHAR(36)UUID
$table->timestamps()created_at + updated_atEloquent 規約
$table->softDeletes()deleted_at論理削除

カラム修飾子

Schema::create('users', function (Blueprint $table) {
    $table->id();

    $table->string('name', 100)
          ->nullable()             // NULL 許可
          ->default('Anonymous')   // デフォルト値
          ->comment('表示名');     // カラムコメント

    $table->string('email')
          ->unique();              // UNIQUE 制約

    $table->integer('age')
          ->unsigned()             // UNSIGNED
          ->index();               // インデックス追加

    $table->string('slug')
          ->after('name');         // 既存カラム直後に追加(alter 時)

    $table->timestamps();
});

外部キー (foreignId / constrained)

Schema::create('articles', function (Blueprint $table) {
    $table->id();
    $table->string('title');
    $table->text('body');

    // 推奨: foreignId + constrained
    $table->foreignId('user_id')
          ->constrained()                  // users.id への FK
          ->onUpdate('cascade')
          ->onDelete('cascade');

    // テーブル名が規約違反の場合
    $table->foreignId('author_id')
          ->constrained('users')           // 明示
          ->nullOnDelete();                // 削除時 NULL に

    // 古典的書き方
    $table->unsignedBigInteger('category_id');
    $table->foreign('category_id')->references('id')->on('categories');

    $table->timestamps();
});

インデックスとユニーク制約

Schema::create('logs', function (Blueprint $table) {
    $table->id();
    $table->string('user_id');
    $table->string('action');
    $table->timestamp('created_at')->useCurrent();

    // 単一カラム
    $table->index('user_id');

    // 複合インデックス
    $table->index(['user_id', 'action']);

    // 名前指定
    $table->index('action', 'idx_action_only');

    // ユニーク
    $table->unique(['user_id', 'action']);

    // 全文検索 (MySQL)
    $table->fullText('body');
});

// 既存テーブルに追加
Schema::table('users', function (Blueprint $table) {
    $table->index('email');
});

// 削除
Schema::table('users', function (Blueprint $table) {
    $table->dropIndex(['email']);              // 自動命名規則
    $table->dropIndex('idx_action_only');      // 明示名
    $table->dropUnique(['user_id', 'action']);
});

既存テーブルの変更 (alter)

// カラム追加
Schema::table('users', function (Blueprint $table) {
    $table->string('phone', 20)->nullable()->after('email');
});

// カラム削除
Schema::table('users', function (Blueprint $table) {
    $table->dropColumn('phone');
    $table->dropColumn(['phone', 'address']);  // 複数
});

// カラム変更(doctrine/dbal が必要、Laravel 11 以降は不要)
Schema::table('users', function (Blueprint $table) {
    $table->string('name', 200)->change();     // VARCHAR(100) → VARCHAR(200)
    $table->string('email')->nullable(false)->change();
});

// カラム名変更
Schema::table('users', function (Blueprint $table) {
    $table->renameColumn('name', 'full_name');
});

// テーブル名変更
Schema::rename('users', 'members');

マイグレーション実行コマンド

コマンド動作本番
php artisan migrate未適用の up() を順次実行
migrate:status適用状況を一覧表示
migrate:rollback直近のバッチを down()
migrate:rollback --step=33 ステップ戻す
migrate:refresh全 rollback + migrate×
migrate:reset全 rollback のみ×
migrate:fresh全テーブル DROP + migrate絶対 ×
migrate --pretendSQL のみ表示(dry-run)確認用
migrate --force本番環境でも実行 (APP_ENV=production 時必須)
# 開発時のサイクル
php artisan migrate:fresh --seed   # 全消し → 再構築 → シーダ

# 本番デプロイ時
php artisan migrate --force         # 確認なしで実行
php artisan migrate:status          # 適用状況確認

# 特定パスのみ
php artisan migrate --path=database/migrations/2026_05_18_create_users_table.php

複数 DB 接続のマイグレーション

// config/database.php に複数接続を定義
// 'connections' => [
//     'mysql'      => [...],
//     'mysql_logs' => [...],
// ]

return new class extends Migration {
    protected $connection = 'mysql_logs';   // ← 接続指定

    public function up(): void {
        Schema::connection('mysql_logs')->create('access_logs', function (Blueprint $table) {
            $table->id();
            $table->string('url');
            $table->timestamp('accessed_at');
        });
    }
};

スキーマダンプ (Laravel 8+)

マイグレーションファイルが大量になったらスキーマダンプで集約できます。

# 現状のスキーマを SQL でダンプ
php artisan schema:dump

# ダンプ後の古い migrate ファイルを削除
php artisan schema:dump --prune

# dump 後の挙動:
# - migrate:fresh は dump を一括実行 → 個別 migrate を流す
# - 大量のマイグレーションを高速に再現できる

本番運用のベストプラクティス

  • down() を必ず書く。緊急ロールバック時に必要
  • migrate:fresh は本番禁止。データ全消去される
  • --force を CI でつける(APP_ENV=production だと確認プロンプトが出る)
  • 大規模 alter はpt-online-schema-change / gh-ost で実施(Laravel の標準は ALTER TABLE で行ロック)
  • カラム削除はリリースを分ける: ①「アプリで参照しないように修正」→ デプロイ → ②「カラム DROP」マイグレーション
  • マイグレーションファイルを過去日付に書き換えない(チームの DB と差分が出る)

FAQ

Q: マイグレーションファイルを間違って上書きしてしまった
A: migrations テーブルから該当行を削除して、再 migrate。チーム共有後なら新規ファイルで修正を。

Q: enum を後から変更したい
A: MySQL の ALTER TABLE で値追加自体は可能だが、Laravel 標準では対応薄。DB::statement("ALTER TABLE ...") で生 SQL を流す。

Q: マイグレーションで初期データを投入したい
A: マイグレーション内で DB::table()->insert() 可能だが、シーダ (Seeder) に分けるのが Laravel 流。

編集
Post Share
子ページ
  1. テーブルの作成と定義の変更
  2. カラムの追加、変更、削除方法
  3. データ型とカラム修飾子
  4. デフォルト値の設定
同階層のページ
  1. インストールと設定
  2. クイックスタート & チュートリアル(初心者向け)
  3. クイックスタート & チュートリアル(中級者向け)
  4. ルーティング
  5. Bladeテンプレート(ビュー/レイアウト)
  6. コントローラー
  7. マイグレーションとテーブル定義
  8. データベースの設定
  9. Eloquentモデル (ORM)
  10. SQLとクエリビルダー
  11. バリデーション
  12. .envファイルの設定値へのアクセス
  13. 動作環境による分岐処理
  14. configフォルダ配下の設定値へのアクセス
  15. assetヘルパーを利用したpublicフォルダへのアクセス
  16. storageフォルダへのアクセス
  17. アプリケーション名の変更
  18. メンテナンス
  19. ログイン画面(認証システム)の作成
  20. ログインの必須化
  21. ログインユーザー情報の取得
  22. ルートの認証化
  23. 本番サーバーへのデプロイ方法
  24. 多言語化
  25. csrf_field
  26. ファイルのダウンロード
  27. CSVのアップロードおよび読み込み(maatwebsite/excel)
  28. ページタイトルの設定
  29. コマンド一覧
  30. エラー一覧
  31. SQLの実行ログ出力方法
  32. キャッシュのクリア
  33. Selectの結果の最初もしくは最後に任意の値を追加する方法
  34. ajaxでPOST通信する際の注意点
  35. ソーシャルログインの実装
  36. セッション情報の確認
  37. ログイン、ユーザー登録、パスワードリセット後のリダイレクト先の変更方法
  38. redirectやreturn viewにメッセージを付与する方法
  39. クッキー(cookie)の設定と取得
  40. クラスの再読み込み
  41. csrfの有効時間を変更する方法
  42. ViewComposerを用いてviewに共通の値を付与する方法
  43. View::shareを用いて共通の値を各ビューに渡す方法
  44. ミドルウェアを用いた処理の共通化
  45. Middleware内でAuth::check()などを使用する方法
  46. Controller以外でリダイレクトする方法
  47. セッションの値の取得/保存/更新/削除
  48. $requestの値を変更する方法
  49. 常時SSL化
  50. ページング(ページネーション)をする方法
  51. vue.jsとの連携
  52. Vue.jsと連携するSPA実行環境構築
  53. .envの値をvue.jsで参照する方法
  54. vue.jsを本番環境にリリースする方法
  55. could not find driver(Windows, MySQL編)