LaravelでDBを扱うとき、よく混ざりがちな3つ。
- Migration → テーブル構造を管理する
- Seeder → 環境に必要なデータを投入する
- Factory → テスト・開発用データを生成する
この3つは役割が明確に違います。
実務目線で整理します。
① Migration — データベース構造のバージョン管理
「DB設計をコードで管理する仕組み」
新規テーブル作成
php artisan make:migration create_posts_table
生成されるファイル:
database/migrations/xxxx_xx_xx_create_posts_table.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('posts', function (Blueprint $table) {
$table->id();
$table->string('title', 255);
$table->text('content');
$table->boolean('is_published')->default(false);
$table->foreignId('user_id')
->constrained()
->onDelete('cascade');
$table->timestamps();
$table->index('is_published');
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};
実行
php artisan migrate
本番
php artisan migrate --force
カラム追加
php artisan make:migration add_status_to_posts_table
Schema::table('posts', function (Blueprint $table) {
$table->string('status')->default('draft')->after('content');
});
カラム変更(注意点)
カラム変更には doctrine/dbal が必要:
composer require doctrine/dbal
$table->string('title', 500)->change();
実務ポイント
- migrationは絶対にPRに含める
- down()も必ず書く
- 外部キー順序に注意
- 本番で
migrate:freshは絶対禁止
② Seeder — 初期データ・マスタデータ投入
「構造ではなく“状態”を作る」
Seeder作成
php artisan make:seeder PostSeeder
実装(冪等性を意識)
use Illuminate\Database\Seeder;
use App\Models\Post;
class PostSeeder extends Seeder
{
public function run(): void
{
Post::updateOrCreate(
['title' => 'Welcome Post'],
[
'content' => 'First seeded post.',
'is_published' => true,
'user_id' => 1,
]
);
}
}
なぜ updateOrCreate?
Seederは何度も実行される可能性があります。
create() → 重複事故
updateOrCreate() → 安全
DatabaseSeederに登録
public function run(): void
{
$this->call([
PostSeeder::class,
]);
}
実行
php artisan db:seed
本番:
php artisan db:seed --force
環境ごとに分ける例
if (app()->environment('production')) {
$this->call(ProductionSeeder::class);
}
Seederの役割例
- 管理者アカウント
- 権限マスタ
- 都道府県マスタ
- 初期設定データ
③ Factory — テストデータの自動生成
「大量データを一瞬で作る」
Factory作成
php artisan make:factory PostFactory --model=Post
実装
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
public function definition(): array
{
return [
'title' => fake()->sentence(),
'content' => fake()->paragraph(),
'is_published' => fake()->boolean(),
'user_id' => \App\Models\User::factory(),
];
}
}
使用例
50件生成
Post::factory()->count(50)->create();
状態を指定
Post::factory()
->count(10)
->state(['is_published' => true])
->create();
Seederと組み合わせる
public function run(): void
{
Post::factory()->count(100)->create();
}
④ 3つの責務比較
| 項目 | Migration | Seeder | Factory |
|---|---|---|---|
| 管理対象 | テーブル構造 | 初期データ | ダミーデータ |
| 本番利用 | ✅ | ✅ | △ |
| 冪等性必要 | - | 必須 | 不要 |
| 目的 | スキーマ管理 | 環境構築 | テスト効率化 |
⑤ RDSを使っている場合
やることは変わりません。
.env:
DB_HOST=xxxx.rds.amazonaws.com
あとは通常通り:
php artisan migrate --force
php artisan db:seed --force
⑥ 実務でよくある事故
❌ migration内でデータ投入
❌ Seederでcreateのみ使用
❌ 本番でmigrate:fresh
❌ 外部キー制約を考慮せずrollback
⑦ 実務ベストプラクティス
- 構造変更はmigrationのみ
- 初期データはSeederで冪等に
- テストはFactoryで大量生成
- 本番は必ず
--force - CI/CDに組み込む
まとめ
LaravelのDB管理は
- 構造
- 状態
- テスト
を明確に分離する思想で設計されています。
この分離を守るだけで、
- 本番事故
- データ不整合
- 再現不能な状態
はかなり防げます。
「なんとなく使う」から
「意図して使い分ける」に変わると、運用の安定度は一気に上がります。