この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:7
ページ更新者:atom
更新日時:2026-06-11 07:10:02

タイトル: マイグレーションとテーブル定義
SEOタイトル: 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

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

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

<?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::create('users', function (Blueprint $table) {
            $table->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 流。