メシのタネ

メシのタネになる、Laravelや設計思想の技術配信サイト


Laravel Seeder & Factory徹底活用ガイド – 退屈な手入力とは永別しよう


  1. Laravel
  2. Laravel Seeder & Factory徹底活用ガイド – 退屈な手入力とは永別しよう

開発環境で「それっぽいテストデータ山盛りにしたり空にしたい」あなたへ——Seeder と Factory を使って秒でデモデータを用意し、ストレスフリーなテスト環境を手に入れましょう。
この記事は ライトに読めるのに実用的 を合言葉に、セットアップから応用テクまで一気にまとめます。

そもそも Seeder / Factory って何者?

Seeder と Factory は、Laravel データ生成界のずっとも。
Factory が「こういうスペックで量産するわ」と自動工作機を持ち込み、
Seeder が「んじゃこのタイミングで50連投!」と DB にタネをばら撒く。
この 2 役を使いこなせば、退屈な INSERT 文の手打ちとは即・決別できます

用語ざっくり役割覚え方
Factoryモデル 1 件分の 設計図 & 自動生成マシーン工場:1 クリックで製品を量産
Seeder量産したデータを いつ・どの順番で・何件流し込むか を管理する司令塔種まき人:畑(DB)に種(データ)をまく
たねまる

Factory が「どうやって作る?」、Seeder が「いつ何個作る?」を担当するんだね〜。

セットアップ

開発環境をシード対応モードに切り替えるためのクイックスタート。
composer.json の健診から Faker 追加、Seeder & Factory 雛形生成まで、5 分で終わる初期設定をここで一気に片付けます。

Fakerのインストール

ダミーデータの値を作るツールです。
👉️ Faker活用ライトガイド – “それっぽい” ダミーデータを 5 秒で錬成する

SeederとFactoryをひな形生成

php artisan make:seeder BookSeeder
php artisan make:factory BookFactory --model=Book

これで database/seedersdatabase/factories にファイルが生えます。

基本の Factory 定義 – Book を 30 冊量産

database/factories/BookFactory.php

BookFactory ではタイトル・著者・刊行日といった本らしい属性を Faker で自動生成。量産した30冊を使って一覧ページやリレーション表示をサクッと検証できます。

<?php

use Illuminate\Database\Eloquent\Factories\Factory;

class BookFactory extends Factory
{
    protected $model = Book::class;

    public function definition(): array
    {
        return [
            'title' => $this->faker->sentence(),
            'author' => $this->faker->name(),
            'published_at' => $this->faker->dateTimeBetween('-10 years'),
            'isbn' => $this->faker->isbn13(),
            'summary' => $this->faker->paragraph(),
        ];
    }
}

量産コマンド(Tinker や Seeder から呼ぶ)

Book::factory()->count(30)->create();

これだけで 30 行 INSERT 完了。ループ地獄と永別です。

Seederで呼び出せる便利さ

BookSeeder は BookFactory を呼び出して 30 冊のダミーデータを DB に一括投入する撒き役。
複数 Seeder を束ねれば php artisan db:seed 一発でフルデータセットが完成する快感を体感できます!

class BookSeeder extends Seeder
{
    public function run(): void
    {
        Book::factory()->count(30)->create();
    }
}

次に、DatabaseSeeder でまとめておくと全体シードがラク。

class DatabaseSeeder extends Seeder
{
    public function run(): void
    {
        $this->call([
            BookSeeder::class,
            UserSeeder::class,
            // ここに他 Seeder を追加
        ]);
    }
}

Seederを準備したら

php artisan migrate:fresh --seed   # テーブル再作成 + 全Seeder実行

上記コマンドを実行してテーブルにデータを入力します。

たねまる

何度も使える最強のSeederをつくろ〜!

応用テク – State, Sequence, Relation の三種盛り

Factory にスパイスを振りかけて、単調なダミーデータを一気に多様化させる3つの技。

  • state() … 1 パターンだけ属性を上書き
  • sequence() … 属性パターンを順番にローテーション
  • リレーションメソッド … 関連モデルを同時に量産

state() で属性の差分だけ上書き

👉️ 1 冊だけgenresci-fiに固定された Book レコードを生成

Book::factory()
    ->state(fn () => ['genre' => 'sci-fi'])
    ->create();

sequence() でパターン回し撃ち

👉️ genrefantasy → mystery → sci-fi と順番にローテーションしながら 6 冊生成される

Book::factory()
    ->count(6)
    ->sequence(
        ['genre' => 'fantasy'],
        ['genre' => 'mystery'],
        ['genre' => 'sci-fi']
    )
    ->create();

Relationを指定して関連モデルにもレコードを量産

👉️ 各BookにReviewが5件ずつ関連付けられて生成される

Book::factory()
    ->hasReviews(5)   // ReviewFactory も自動呼び出し
    ->create();

関係を成り立たせるための準備

  1. Book モデルreviews()hasMany リレーションを追加。※1
  2. ReviewFactory を作成し、book_id を必ずセット(Book::factory() で OK)。※2
  3. 上記 2 点が揃うと、hasReviews(5) が ReviewFactory を 5 回呼び出し、生成後に外部キーを自動で書き込みます。

Book モデルのリレーション(※1)

class Book extends Model
{
    use HasFactory;

    public function reviews(): HasMany
    {
        return $this->hasMany(Review::class);
    }
}

database/factories/ReviewFactory.php を追加(※2)

use Illuminate\Database\Eloquent\Factories\Factory;

class ReviewFactory extends Factory
{
    protected $model = Review::class;

    public function definition(): array
    {
        return [
            'book_id' => Book::factory(),  // Book と紐付け
            'rating'  => $this->faker->numberBetween(1, 5),
            'comment' => $this->faker->sentence(),
        ];
    }
}

テスト連携 – RefreshDatabase だけが正義ではない

テストごとに DB をリセットする方法は主に 2 種類 あります。

Trait仕組みどんな時に便利?
RefreshDatabase各テスト前後で migrate:fresh を実行。Seeder も走らせてフルリセット外部キー制約・マルチ DB・ファイルストレージなどを絡めた統合テストに◎
DatabaseTransactions各テストをトランザクションで包み、終了時に自動ロールバック単体テスト中心で高速に回したいときに◎

RefreshDatabase の例

このテストは RefreshDatabase を採用。毎回 migrate:fresh と Seeder 実行で DB をゼロから再構築するため、外部キー制約や複数接続を含む統合テストに向く。ただし実行時間はやや長め。

use Illuminate\Foundation\Testing\RefreshDatabase;

class HomeScreenTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_shows_books()
    {
        $this->seed(BookSeeder::class);

        $response = $this->get('/');
        $response->assertStatus(200);
    }
}

DatabaseTransactions の例

こちらは DatabaseTransactions を使用。各テストをトランザクションでラップし、終了時に自動ロールバックするので高速。ただし非同期ジョブや別接続が絡むケースではロールバックが届かない点に注意。

use Illuminate\Foundation\Testing\DatabaseTransactions;

class HomeScreenFastTest extends TestCase
{
    use DatabaseTransactions;

    public function test_it_shows_books_quickly()
    {
        Book::factory()->create();

        $response = $this->get('/');
        $response->assertStatus(200);
    }
}
たねまる

キュー処理や別コネクションが飛び交うテストではトランザクションが効かないからRefreshDatabaseを選ぶのがオススメだけど
テーブル多いなら、テーブル作ったままテストで追加したレコードだけを消せるDatabaseTransactionも便利だよ〜。

外部キー制約でエラーになる (SQLSTATE[23000]: Integrity constraint violation)

\Illuminate\Database\Eloquent\Model::unguard() でガードを外すか、Seeder の実行順序を見直して 親テーブルを先に作成 しましょう。

Faker の unique() が枯渇して Unique constraint violation が起きる

FakerのUniqueには、上限があります。(文字列は数千〜数万辞書の程度によります)


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください