↔️

Eloquent の Factory で関連モデルも同時に生成する方法

2023/12/10に公開

こんにちは。 SAW です。
最近、 Gran Turismo 7 の National B Lincense をすべてゴールドでクリアしました。
結構難しくて苦戦しました (笑)

Laravel で Faker と Eloquent の Factory を使うと、簡単にダミーデータを大量に生成できます。

関連モデルのダミーデータを生成したい場合、参照先の FOREIGN KEY の値を知っている必要があります。
しかし、 参照先のモデルのダミーデータがテーブルに挿入するまで FOREIGN KEY の値は確定しません。

本記事では、 Eloquent の Factory を使ってダミーデータを生成する際に、同時に関連モデルのダミーデータも生成する方法を紹介します。
なお、本記事では Laravel のバージョンが 10.x 系 を前提としています。

対象読者

本記事で想定する読者層は次の通りです。

  • Laravel の基本的な知識を有している
  • Factory を利用した seeding についての基本的な知識を有している

前準備: テーブルのスキーマと Eloquent Model の定義

テーブルのスキーマ定義

まず、 postscomments テーブルのスキーマを以下のように定義します。

posts テーブルのスキーマ定義
Schema::create('posts', function (Blueprint $table) {
    $table->ulid('id')->primary();
    $table->string('title');
    $table->string('content');
    $table->timestamps();
});
comments テーブルのスキーマ定義
Schema::create('comments', function (Blueprint $table) {
    $table->ulid('id')->primary();
    $table->foreignUlid('post_id');
    $table->string('reply');
    $table->timestamps();
});

postscomments の関連は以下の ER 図の通りです。

Eloquent Model の定義

テーブルに対応する Post クラスと Comment クラスの定義はそれぞれ以下の通りです。
Eloquent Model で関連を表すために、 comments()Post クラスに定義します。

app/Models/Post.php
class Post extends Model
{
    use HasFactory, HasUlids;

    public function comments(): HasMany
    {
        return $this->hasMany(Comment::class);
    }
}
app/Models/Comment.php
class Comment extends Model
{
    use HasFactory, HasUlids;
}

Factory クラスの定義

PostComment のモデルを生成する PostFactory クラスと CommentFactory クラスをそれぞれ以下のように定義します。

database/factories/PostFactory.php
class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'id' => Str::ulid(),
            'title' => fake()->sentence(),
            'content' => fake()->text(),
        ];
    }
}
database/factories/CommentFactory.php
class CommentFactory extends Factory
{
    public function definition(): array
    {
        return [
            'id' => Str::ulid(),
            'reply' => fake()->text(),
        ];
    }
}

Factory で関連モデルも生成する方法

モデルを factory() を利用して生成する場合、 has() を利用することで関連モデルもあわせて生成できます。
has() を利用することで、事前に参照先の FOREIGN KEY を知らなくても、自動的に関連づけをしてモデルが生成されます。

has() を利用する際には、引数に関連モデルの Factory オブジェクトを渡します。
例えば、 前準備: テーブルのスキーマと Eloquent Model の定義 で定義した Post モデルと関連モデルの Comment を生成する場合、 has() の引数に CommentFactory オブジェクトを渡します。
CommentFactory オブジェクトは Comment::factory() で生成できます。

以下は 3 つの Comment を持つ Post を 5 つ生成するコード例です。
Post は 5 個、 Comment は 15 個生成されます。

Factory で Post と関連モデルの Comment を生成するコード例
Post::factory(5)
    ->has(Comment::factory(3))
    ->create();

tinker などで確認してみると、 Post が 5 個、 Comment が 15 個生成されていることが確認できます。

Post と Comment の要素数を tinker で確認した結果
> Post::count()
= 5

> Comment::count()
= 15

また、以下の実行結果から、それぞれの PostComment を 3 つ持っていることも確認できます。

Post が持つ Comment の要素数をカウントした結果
> Post::all()->map(fn($x) => $x->comments->count())
= Illuminate\Support\Collection {#7300
    all: [
      3,
      3,
      3,
      3,
      3,
    ],
  }

まとめ

本記事のまとめは次の通りです。

  • Factory の has() を利用することで関連モデルも同時に生成できる
    • 参照先のモデルの FOREIGN KEY を知らなくても自動的に関連づけしてモデルが生成される

Eloquent の Factory と少し仲良くなれた気がします (笑)

参考文献

https://laravel.com/docs/master/eloquent-factories#factory-relationships

Discussion