Laravelのfactory(faker)で3階層以上のテストデータを作る
Laravelのファクトリ便利ですよね。
fakerでテストデータ作るのもラクチンです。
ただ、User->Post->Comment
のような3階層構造のリレーションモデルを作るときに悩みました。
結論
いろいろと試行錯誤した結果、今後はこのコードで作成しようと思います。
seederまたはtestcode
factory(User::class, 3)->create()->each(function ($user) {
factory(Post::class, 2)->create(['user_id' => $user->id])->each(function ($post){
factory(Comment::class, 3)->create(['post_id' => $post->id]);
});
});
下記リレーション構造を持つUserが3つ出来上がります。
- User
- Post
- Comment
- Comment
- Comment
- Post
- Comment
- Comment
- Comment
メリット
- 書き方が統一されている
- 何階層でもOK
- 数値部分を変えれば各件数も思いのまま
デメリット
- 親IDのカラム名漏れ(でも変更しないだろうし、テストコードだし、いいよね?)
経緯
いつもお世話になっているReaDoubleのサンプルコードを参考にしたのですが、いろいろ問題がありました。
サンプルコードでは2階層までは作れたが
ReaDoubleのsample
$users = factory(App\User::class, 3)->create()->each(function ($u) {
$u->posts()->save(factory(App\Post::class)->make());
});
- 親はcreate、子はmakeでわかりずらい
- Postを複数作りたいときはsaveManyにしないといけない
$user->posts()->saveMany(factory(Post::class, 2)->make());
3階層以上はどうすればいいのかわからなかった
試行錯誤したがエラーとなるコード
factory(User::class, 2)->create()->each(function ($user) {
$user->posts()->saveMany(factory(Post::class, 2)->make()->each(function ($post) {
$post->comments()->saveMany(factory(Comment::class, 2)->make());
}));
});
- 親IDがNULL的なエラーが発生する・・・。
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'post_id' cannot be null
ファクトリ定義の中で作成する方法もあったけど、、
ReaDoubleのsample
$factory->define(App\Post::class, function ($faker) {
return [
'title' => $faker->title,
'content' => $faker->paragraph,
'user_id' => function () {
return factory(App\User::class)->create()->id;
}
];
});
- 常に親モデルをcreateすることになってしまう
- そもそもファクトリ定義はモデル毎に分離しておきたい
- 3階層以上だと出来てもカオスになりそう
ということで
冒頭のコードにたどり着きました。
他に良い方法がありましたら教えてください。
それではステキなLaravelライフを。