factoryを使ったダミーデータ作成方法の3回目です。今回は、複数のデータ作成に便利なsequenceメソッドと、そのユースケースをご紹介します。


以前の投稿はこちらから閲覧できます。(1)(2)

前回、同じく複数データを作成するcreateManyメソッドを使った記述をご紹介しました。

少し振り返りになりますが、name属性がそれぞれ「高橋・佐藤・田中」のデータをcreateManyで作成する場合、以下のように記述します。

        User::factory()->createMany([
            ['name' => '高橋'],
            ['name' => '佐藤'],
            ['name' => '田中'],
        ]);

作成したいデータを配列でcreateManyに渡すことで、簡潔に記述することができます。

では、sequenceで同じデータを作る場合はどのようにするのでしょうか。

sequenceで複数データを作成

先ほどと同じ、name属性が「高橋・佐藤・田中」のデータを作成してみましょう。以下のように記述します。

        User::factory()
            ->count(3)
            ->sequence(
                ['name' => '高橋'],
                ['name' => '佐藤'],
                ['name' => '田中']
            )
            ->create();

countメソッドに作成件数を指定し、sequenceには作成したいデータを渡します。またcreateメソッドも繋ぐ必要があります。

このケースではcreateManyと比較して、sequenceの方がやや冗長に見えますよね。ですが、以下のようなケースならどうでしょう。

sequenceが適しているケース(1)

sex属性が「女性」と「解答なし」のデータをそれぞれ3件、全6件分作成したい。このようなケースでは、sequenceを使って以下のように記述できます。

        User::factory()
            ->count(6)
            ->sequence(
                ['sex' => '女性'],
                ['sex' => '解答なし'],
            )
            ->create();

tinkerで実行すると、「女性・解答なし・女性・解答なし….」と繰り返すデータがちゃんと6件作成されていることがわかります。

= Illuminate\Database\Eloquent\Collection {#29800
    all: [
      App\Models\User {#29813
        sex: "女性",
        name: "三宅 涼平",
        id: 142,
      },
      App\Models\User {#29793
        sex: "解答なし",
        name: "渡辺 充",
        id: 143,
      },
      App\Models\User {#29777
        sex: "女性",
        name: "近藤 裕樹",
        id: 144,
      },
      App\Models\User {#29845
        sex: "解答なし",
        name: "伊藤 加奈",
        id: 145,
      },
      App\Models\User {#29771
        sex: "女性",
        name: "三宅 加奈",
        id: 146,
      },
      App\Models\User {#29781
        sex: "解答なし",
        name: "青田 くみ子",
        id: 147,
      },

sequenceを使わなくても、以下のように「女性」のレコード・「解答なし」のレコードをそれぞれで作成しても良いかもしれません。

       User::factory(3)->create(['sex' => '女性']);
       User::factory(3)->create(['sex' => '解答なし']);

ただ、その場合作成されたレコード群は「女性・女性・女性・解答なし・解答なし・解答なし」と不自然にデータが固まってしまうので、テストケースによってどちらにするかは使い分けるとよさそうです。

ちなみに、今のようなデータをcreateManyを使って作成しようとすると、

       User::factory()->createMany([
            ['sex' => '女性'],
            ['sex' => '解答なし'],
            ['sex' => '女性'],
            ['sex' => '解答なし'],
            ['sex' => '女性'],
            ['sex' => '解答なし'],
        ]);

このように6件分のデータを1つ1つ配列に渡さないといけないので、より冗長になってしまいます。

sequenceが適しているケース(2)

もう1つのsequenceが適しているケースは、「データに一意性を持たせたい場合」です。

以下は、私が関わらせていただいているプロジェクトで使われているコードを本記事用に少し変更したものですが、sequenceの使用例としてまさにぴったりです。

テスト用に47都道府県のレコードを作成したい。県名は、連続した一意性のあるデータとしたい。そういう場合、以下のようなシンプルな記述で作成できます。

        Prefecture::factory()->count(47)
            ->sequence(function ($sequence) {
                return ['name' => "都道府県{$sequence->index}"];
            })->create();

上記のコードではクロージャーで引数として$sequenceを受け取っています。$sequenceオブジェクトが持つindexプロパティを使用し、県名に連番を割り当てています。

以下はtinkerの実行結果です。「都道府県0、都道府県1、都道府県2、都道府県3、都道府県4、、、」と連続した番号が割り当てられることで、それぞれのデータに一意性を持たせることができます。

= Illuminate\Database\Eloquent\Collection {#29831
    all: [
      App\Models\Prefecture {#29822
        id: 20,
        name: "都道府県0",
      },
      App\Models\Prefecture {#29825
        id: 21,
        name: "都道府県1",
      },
      App\Models\Prefecture {#29821
        id: 22,
        name: "都道府県2",
      },
      App\Models\Prefecture {#29820
        id: 23,
        name: "都道府県3",
      },
      App\Models\Prefecture {#29819
        id: 24,
        name: "都道府県4",
      },
      App\Models\Prefecture {#29818
        id: 25,
        name: "都道府県5",
      },
      App\Models\Prefecture {#29817
        id: 26,
        name: "都道府県6",
      },
      App\Models\Prefecture {#29816
        id: 27,
        name: "都道府県7",
      },
      .
      .
      .

$sequenceindexプロパティは「0」始まりなので、連番の始まりを「1」にしたい場合は、コールバック関数の中で +1 としてください。

            ->sequence(function ($sequence) {
                return ['name' => "都道府県" . ($sequence->index + 1)];
            })

sequenceはそれほど出番が多いメソッドではないかもしれませんが、適した箇所に使うととっても便利です。ぜひご活用ください。

By hmatsu