こんにちは、かつコーチです。
前回は多対多リレーション(belongsToMany)を扱いました。
実際にリレーションの動きを確認しようとすると、記事・タグ・中間テーブルにそれぞれデータを手入力するのは大変ですよね。
そこで今回は、テストデータをまとめて投入できる Seeder を扱います。
Seederとは?
なぜ手動でデータを入れてはいけないのか
Seederとは、データベースにテスト用のデータをまとめて投入する仕組みです。
phpMyAdminなどのGUIツールで1件ずつ手入力することもできますが、これにはいくつか問題があります。
- チームメンバーごとにデータの内容がバラバラになる
- 環境を作り直すたびに、また手作業で入れ直す必要がある
- 「このデータ、どうやって入れたんだっけ?」が分からなくなる
Seederを使えば、投入するデータの内容がコードとして残るため、誰が・いつ実行しても同じ状態を再現できます。
私が実務に入りたてのころ、ローカル環境を作り直すたびにテストデータを手打ちしていて、ある日「あのユーザー、パスワード何だっけ」と分からなくなったことがあります。
Seederの存在を知ってからは、この手間から完全に解放されました。
Seederの置き場所
Seederファイルは database/seeders/ に置かれます。
Laravelプロジェクトを作成すると、最初から DatabaseSeeder.php という起点となるファイルが用意されています。
database/
└── seeders/
└── DatabaseSeeder.php
ここに他のSeederを呼び出す処理を書いていくのが基本の流れです。
方法1:直接データを書く(シンプルな固定データ向き)
基本の書き方
まずは一番シンプルな方法として、Seederファイルの中に直接データを書くやり方です。
管理者アカウントやカテゴリマスタなど、「いつでも同じ内容で入れたい固定データ」に向いています。
php artisan make:seeder TagSeeder
<?php
// database/seeders/TagSeeder.php
namespace Database\Seeders;
use App\Models\Tag;
use Illuminate\Database\Seeder;
class TagSeeder extends Seeder
{
public function run(): void
{
Tag::insert([
['name' => 'Laravel', 'created_at' => now(), 'updated_at' => now()],
['name' => 'PHP', 'created_at' => now(), 'updated_at' => now()],
['name' => 'MySQL', 'created_at' => now(), 'updated_at' => now()],
]);
}
}
insert() を使うと、複数件のデータを1回のクエリでまとめて登録できます。
DatabaseSeederから呼び出す
作ったSeederは、DatabaseSeeder.php の run() メソッドから呼び出すことで実行対象になります。
<?php
// database/seeders/DatabaseSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
TagSeeder::class,
]);
}
}
php artisan db:seed
$this->call() に配列でSeederクラスを渡すことで、実行順序をコントロールしながらまとめて実行できます。
方法2:Factoryと組み合わせる(大量データ向き)
なぜ直接書く方法だけでは足りないのか
方法1は固定データには向いていますが、「ユーザーを100人分作りたい」というケースでは現実的ではありません。
こういった大量データの生成には、次回詳しく扱う Factory と組み合わせるのが定番です。
<?php
// database/seeders/UserSeeder.php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
public function run(): void
{
User::factory()->count(50)->create();
}
}
User::factory()->count(50)->create() の1行だけで、ダミーの氏名・メールアドレスが入った50人分のユーザーが作成されます。
Factory自体の仕組みは次の記事で扱うので、ここでは「Seeder単体では固定データ向き、Factoryと組み合わせると大量データ向き」という役割分担だけ押さえておいてください。
方法3:CSVやJSONなど外部ファイルから読み込む
既存データを移行したいときに使う
社内に既にあるExcelやCSVのマスタデータを、そのままテストデータとして使いたい場面もあります。
そんなときは、外部ファイルを読み込んでSeederに流し込む方法が便利です。
<?php
// database/seeders/PrefectureSeeder.php
namespace Database\Seeders;
use App\Models\Prefecture;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\File;
class PrefectureSeeder extends Seeder
{
public function run(): void
{
$path = database_path('seeders/data/prefectures.csv');
$rows = array_map('str_getcsv', File::lines($path)->toArray());
// 1行目はヘッダーなので除外する
$header = array_shift($rows);
foreach ($rows as $row) {
$data = array_combine($header, $row);
Prefecture::create([
'code' => $data['code'],
'name' => $data['name'],
]);
}
}
}
code,name
01,北海道
13,東京都
27,大阪府
File::lines() でCSVファイルを1行ずつ読み込み、str_getcsv でカンマ区切りの配列に変換しています。
都道府県マスタや業種マスタなど、「アプリの仕様として決まっている固定リスト」を扱うときに向いている方法です。
つまずきやすいポイント:外部キー制約でエラーになる
実行順序を意識していないと失敗する
複数のSeederを組み合わせたとき、私が実際にハマったのが外部キー制約のエラーです。
❌ Before:親テーブルより先に子テーブルのSeederを実行する
<?php
// database/seeders/DatabaseSeeder.php
public function run(): void
{
$this->call([
ArticleTagSeeder::class, // 中間テーブル(先に実行してしまう)
TagSeeder::class, // tagsテーブル
ArticleSeeder::class, // articlesテーブル
]);
}
SQLSTATE[23000]: Integrity constraint violation:
1452 Cannot add or update a child row: a foreign key constraint fails
article_tag テーブルは articles と tags の両方のIDを参照しているため、参照先のデータがまだ存在しない状態でINSERTしようとすると、この外部キー制約エラーが発生します。
✅ After:参照される側(親)から順番に実行する
<?php
// database/seeders/DatabaseSeeder.php
public function run(): void
{
$this->call([
TagSeeder::class, // 先にtagsテーブル
ArticleSeeder::class, // 先にarticlesテーブル
ArticleTagSeeder::class, // 最後に中間テーブル
]);
}
$this->call() に渡す配列の順序が、そのまま実行順序になります。
「参照される側(親)を先に、参照する側(子)を後に」という原則を意識するだけで、この手のエラーは避けられます。
応用:特定のSeederだけを実行する
開発中に毎回全部実行すると時間がかかる
開発が進んでSeederの数が増えてくると、php artisan db:seed で毎回すべてを実行するのは非効率になってきます。
そんなときは、--class オプションで実行対象を絞り込めます。
# TagSeederだけを実行する
php artisan db:seed --class=TagSeeder
# マイグレーションをやり直してからSeederも実行する
php artisan migrate:fresh --seed
migrate:fresh --seed は、テーブルを全部作り直してから DatabaseSeeder を実行してくれるコマンドです。
「DB構造を変えたので、一度まっさらな状態から作り直したい」というときによく使うので、ぜひ覚えておいてください。
まとめ
この記事のポイント
- Seederはテストデータをコードとして再現可能にする仕組み
- 固定データは「直接書く」方法、大量データは「Factoryとの組み合わせ」が向いている
- 既存の外部データはCSV・JSON読み込みで流し込める
- 複数Seederを組み合わせるときは、外部キー制約を考えて親→子の順に実行する
migrate:fresh --seedでDBを作り直してからテストデータを再投入できる
次に読むべき記事
大量データを効率よく生成する仕組みとして、次はFactoryを詳しく見ていきましょう。
→ 次の記事:Factoryでダミーデータを効率よく作る