Laravel 10.x Eloquent:ファクトリ

イントロダクション

アプリケーションのテストやデータベースの初期値生成時に、データベースへレコードを挿入する必要がある場合があるでしょう。Laravelでは、各カラムの値を手作業で指定する代わりに、Eloquentモデルそれぞれに対して、モデルファクトリを使用し、デフォルト属性セットを定義できます。

ファクトリの作成方法の例を確認するには、アプリケーションのdatabase/factories/UserFactory.phpファイルを見てください。このファクトリはすべての新しいLaravelアプリケーションに含まれており、以下のファクトリ定義が含まれています。

namespace Database\Factories;

use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    /**
     * モデルのデフォルト状態の定義
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
            'remember_token' => Str::random(10),
        ];
    }
}

ご覧のとおり、一番基本的な形式では、ファクトリはLaravelの基本ファクトリクラスを拡張し、definitionメソッドを定義するクラスです。definitionメソッドは、ファクトリを使用してモデルを作成するときに適用する必要がある属性値のデフォルトセットを返します。

fakeヘルパを使うと、ファクトリでFaker PHPライブラリにアクセスでき、テストやシードのためにさまざまな種類のランダムデータを生成でき、便利です。

Note: config/app.php設定ファイルにfaker_localeオプションを追加することで、アプリケーションのFakerロケールを設定できます。

モデルファクトリの定義

ファクトリの生成

ファクトリを作成するには、make:factory Artisanコマンドを実行します。

php artisan make:factory PostFactory

新しいファクトリクラスは、database/factoriesディレクトリに配置されます。

モデルと対応するファクトリの規約

ファクトリを定義したら、モデルのファクトリインスタンスをインスタンス化するために、Illuminate\Database\Eloquent\Factories\HasFactoryトレイトが、モデルへ提供しているstaticなfactoryメソッドが使用できます。

HasFactoryトレイトのfactoryメソッドは規約に基づいて、その トレイトが割り当てられているモデルに適したファクトリを決定します。具体的には、Database\Factories名前空間の中でモデル名と一致するクラス名を持ち、サフィックスがFactoryであるファクトリを探します。この規約を特定のアプリケーションやファクトリで適用しない場合は、モデルのnewFactoryメソッドを上書きし、モデルと対応するファクトリのインスタンスを直接返してください。

use Illuminate\Database\Eloquent\Factories\Factory;
use Database\Factories\Administration\FlightFactory;

/**
 * モデルの新ファクトリ・インスタンスの生成
 */
protected static function newFactory(): Factory
{
    return FlightFactory::new();
}

次に、対応するファクトリで、modelプロパティを定義します。

use App\Administration\Flight;
use Illuminate\Database\Eloquent\Factories\Factory;

class FlightFactory extends Factory
{
    /**
     * このファクトリに対応するモデル名
     *
     * @var class-string<\Illuminate\Database\Eloquent\Model>
     */
    protected $model = Flight::class;
}

ファクトリの状態

状態操作メソッドを使用すると、モデルファクトリへ任意の組み合わせで適用できる個別の変更を定義できます。たとえば、Database\Factories\UserFactoryファクトリに、デフォルトの属性値の1つを変更するsuspended状態メソッドが含まれているとしましょう。

状態変換メソッドは通常、Laravelの基本ファクトリクラスが提供するstateメソッドを呼び出します。stateメソッドは、このファクトリ用に定義する素の属性の配列を受け取るクロージャを受け入れ、変更する属性の配列を返す必要があります。

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * ユーザーが一時停止されていることを示す
 */
public function suspended(): Factory
{
    return $this->state(function (array $attributes) {
        return [
            'account_status' => 'suspended',
        ];
    });
}

「ゴミ箱入り」状態

Eloquentモデルがソフトデリート可能であれば、組み込み済みの「ゴミ箱入り(trashed)」状態メソッドを呼び出し、作成したモデルが既に「ソフトデリート済み」と示せます。すべてのファクトリで自動的に利用可能で、trashed状態を手作業で定義する必要はありません。

use App\Models\User;

$user = User::factory()->trashed()->create();

ファクトリのコールバック

ファクトリコールバックは、afterMakingメソッドとafterCreatingメソッドを使用して登録し、モデルの作成または作成後に追加のタスクを実行できるようにします。ファクトリクラスでconfigureメソッドを定義して、これらのコールバックを登録する必要があります。ファクトリがインスタンス化されるときにLaravelが自動的にこのメソッドを呼び出します。

namespace Database\Factories;

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

class UserFactory extends Factory
{
    /**
     * モデルファクトリの設定
     */
    public function configure(): static
    {
        return $this->afterMaking(function (User $user) {
            // ...
        })->afterCreating(function (User $user) {
            // ...
        });
    }

    // ...
}

また、stateメソッド内にファクトリ・コールバックを登録し、特定の状態に特化した追加タスクを実行することもできます。

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * このユーザーが資格停止中かを示す
 */
public function suspended(): Factory
{
    return $this->state(function (array $attributes) {
        return [
            'account_status' => 'suspended',
        ];
    })->afterMaking(function (User $user) {
        // ...
    })->afterCreating(function (User $user) {
        // ...
    });
}

ファクトリを使用するモデル生成

モデルのインスタンス化

ファクトリを定義したら、そのモデルのファクトリインスタンスをインスタンス化するために、Illuminate\Database\Eloquent\Factories\HasFactoryトレイトにより、モデルが提供する静的なfactoryメソッドを使用できます。モデル作成のいくつかの例を見てみましょう。まず、makeメソッドを使用して、データベースへ永続化せずにモデルを作成します。

use App\Models\User;

$user = User::factory()->make();

countメソッドを使用して多くのモデルのコレクションを作成できます。

$users = User::factory()->count(3)->make();

状態の適用

状態のいずれかをモデルに適用することもできます。モデルへ複数の状態変換を適用する場合は、状態変換メソッドを直接呼び出すだけです。

$users = User::factory()->count(5)->suspended()->make();

属性のオーバーライド

モデルのデフォルト値の一部をオーバーライドしたい場合は、値の配列をmakeメソッドに渡してください。指定された属性のみが置き換えられ、残りの属性はファクトリで指定したデフォルト値へ設定したままになります。

$user = User::factory()->make([
    'name' => 'Abigail Otwell',
]);

もしくは、stateメソッドをファクトリインスタンスで直接呼び出して、インライン状態変更を実行することもできます。

$user = User::factory()->state([
    'name' => 'Abigail Otwell',
])->make();

Note: 複数代入保護は、ファクトリを使用してのモデル作成時、自動的に無効になります。

モデルの永続化

createメソッドはモデルインスタンスをインスタンス化し、Eloquentのsaveメソッドを使用してデータベースへ永続化します。

use App\Models\User;

// App\Models\Userインスタンスを1つ生成
$user = User::factory()->create();

// App\Models\Userインスタンスを3つ生成
$users = User::factory()->count(3)->create();

属性の配列をcreateメソッドに渡すことで、ファクトリのデフォルトのモデル属性をオーバーライドできます。

$user = User::factory()->create([
    'name' => 'Abigail',
]);

連続データ

モデルを生成するごとに、特定のモデル属性の値を変更したい場合があります。これは、状態変換を連続データとして定義することで実現できます。たとえば、作成されたユーザーごとに、adminカラムの値をYNの間で交互に変更したいとしましょう。

use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
                ->count(10)
                ->state(new Sequence(
                    ['admin' => 'Y'],
                    ['admin' => 'N'],
                ))
                ->create();

この例では、admin値がYのユーザーが5人作成され、admin値がNのユーザーが5人作成されます。

必要に応じて、シーケンス値としてクロージャを含めることができます。新しい値をそのシーケンスが必要とするたびにクロージャを呼び出します。

use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
                ->count(10)
                ->state(new Sequence(
                    fn (Sequence $sequence) => ['role' => UserRoles::all()->random()],
                ))
                ->create();

シーケンスクロージャ内では,クロージャへ注入されるシーケンスインスタンスの$indexまたは$countプロパティにアクセスできます。$indexプロパティには、これまでに行われたシーケンスの反復回数が格納され、$countプロパティには、シーケンスが起動された合計回数が格納されます。

$users = User::factory()
                ->count(10)
                ->sequence(fn (Sequence $sequence) => ['name' => 'Name '.$sequence->index])
                ->create();

使いやすいように、シーケンスは、sequenceメソッドを使用して適用することもできます。このメソッドは、内部的にstateメソッドを呼び出すだけです。sequenceメソッドには、クロージャまたはシーケンスの属性を表す配列を指定します。

$users = User::factory()
                ->count(2)
                ->sequence(
                    ['name' => 'First User'],
                    ['name' => 'Second User'],
                )
                ->create();

リレーションのファクトリ

Has Manyリレーション

次に、Laravelの流暢(fluent)なファクトリメソッドを使用して、Eloquentモデルのリレーションを構築する方法を見ていきましょう。まず、アプリケーションにApp\Models\UserモデルとApp\Models\Postモデルがあると想定します。また、UserモデルがPostとのhasManyリレーションを定義していると想定しましょう。 Laravelのファクトリが提供するhasメソッドを使用して、3つの投稿を持つユーザーを作成できます。hasメソッドはファクトリインスタンスを引数に取ります。

use App\Models\Post;
use App\Models\User;

$user = User::factory()
            ->has(Post::factory()->count(3))
            ->create();

規約により、Postモデルをhasメソッドに渡す場合、LaravelはUserモデルにリレーションを定義するpostsメソッドが存在していると想定します。必要に応じ、操作するリレーション名を明示的に指定できます。

$user = User::factory()
            ->has(Post::factory()->count(3), 'posts')
            ->create();

もちろん、関連モデルで状態を操作することもできます。さらに、状態変更で親モデルへのアクセスが必要な場合は、クロージャベースの状態変換が渡せます。

$user = User::factory()
            ->has(
                Post::factory()
                        ->count(3)
                        ->state(function (array $attributes, User $user) {
                            return ['user_type' => $user->type];
                        })
            )
            ->create();

マジックメソッドの使用

使いやすいように、Laravelのマジックファクトリリレーションメソッドを使用してリレーションを構築できます。たとえば、以下の例では、規約を使用して、Userモデルのpostsリレーションメソッドを介して作成する必要がある関連モデルを決定します。

$user = User::factory()
            ->hasPosts(3)
            ->create();

マジックメソッドを使用してファクトリリレーションを作成する場合、属性の配列を渡して、関連モデルをオーバーライドできます。

$user = User::factory()
            ->hasPosts(3, [
                'published' => false,
            ])
            ->create();

状態の変更で親モデルへのアクセスが必要な場合は、クロージャベースの状態変換を提供できます。

$user = User::factory()
            ->hasPosts(3, function (array $attributes, User $user) {
                return ['user_type' => $user->type];
            })
            ->create();

Belongs Toリレーション

ファクトリを使用して"has many"リレーションを構築する方法を検討したので、逆の関係を調べてみましょう。forメソッドを使用して、ファクトリが作成したモデルの属する親モデルを定義できます。たとえば、1人のユーザーに属する3つのApp\Models\Postモデルインスタンスを作成できます。

use App\Models\Post;
use App\Models\User;

$posts = Post::factory()
            ->count(3)
            ->for(User::factory()->state([
                'name' => 'Jessica Archer',
            ]))
            ->create();

作成するモデルに関連付ける必要のある親モデルインスタンスがすでにある場合は、モデルインスタンスをforメソッドに渡すことができます。

$user = User::factory()->create();

$posts = Post::factory()
            ->count(3)
            ->for($user)
            ->create();

マジックメソッドの使用

便利なように、Laravelのマジックファクトリリレーションシップメソッドを使用して、"belongs to"リレーションシップを定義できます。たとえば、以下の例では、3つの投稿がPostモデルのuserリレーションに属する必要があることを規約を使用して決定しています。

$posts = Post::factory()
            ->count(3)
            ->forUser([
                'name' => 'Jessica Archer',
            ])
            ->create();

Many To Manyリレーション

has manyリレーションと同様に、"many to many"リレーションは、hasメソッドを使用して作成できます。

use App\Models\Role;
use App\Models\User;

$user = User::factory()
            ->has(Role::factory()->count(3))
            ->create();

ピボットテーブルの属性

モデルをリンクするピボット/中間テーブルへ設定する属性を定義する必要がある場合は、hasAttachedメソッドを使用します。このメソッドは、ピボットテーブルの属性名と値の配列を2番目の引数に取ります。

use App\Models\Role;
use App\Models\User;

$user = User::factory()
            ->hasAttached(
                Role::factory()->count(3),
                ['active' => true]
            )
            ->create();

状態変更で関連モデルへのアクセスが必要な場合は、クロージャベースの状態変換を指定できます。

$user = User::factory()
            ->hasAttached(
                Role::factory()
                    ->count(3)
                    ->state(function (array $attributes, User $user) {
                        return ['name' => $user->name.' Role'];
                    }),
                ['active' => true]
            )
            ->create();

作成しているモデルへアタッチしたいモデルインスタンスがすでにある場合は、モデルインスタンスをhasAttachedメソッドへ渡せます。この例では、同じ3つの役割が3人のユーザーすべてに関連付けられます。

$roles = Role::factory()->count(3)->create();

$user = User::factory()
            ->count(3)
            ->hasAttached($roles, ['active' => true])
            ->create();

マジックメソッドの使用

利便性のため、Laravelのマジックファクトリリレーションメソッドを使用して、多対多のリレーションを定義できます。たとえば、次の例では、関連するモデルをUserモデルのrolesリレーションメソッドを介して作成する必要があることを規約を使用して決定します。

$user = User::factory()
            ->hasRoles(1, [
                'name' => 'Editor'
            ])
            ->create();

ポリモーフィックリレーション

ポリモーフィックな関係もファクトリを使用して作成できます。ポリモーフィックな"morph many"リレーションは、通常の"has many"リレーションと同じ方法で作成します。たとえば、 App\Models\PostモデルがApp\Models\CommentモデルとmorphMany関係を持っている場合は以下のようになります。

use App\Models\Post;

$post = Post::factory()->hasComments(3)->create();

Morph Toリレーション

マジックメソッドを使用してmorphTo関係を作成することはできません。代わりに、forメソッドを直接使用し、関係の名前を明示的に指定する必要があります。たとえば、CommentモデルにmorphTo関係を定義する commentableメソッドがあると想像してください。この状況でforメソッドを直接使用し、1つの投稿に属する3つのコメントを作成できます。

$comments = Comment::factory()->count(3)->for(
    Post::factory(), 'commentable'
)->create();

ポリモーフィック多対多リレーション

ポリモーフィック「多対多」(morphToManymorphedByMany)リレーションは、ポリモーフィックではない「多対多」リレーションと同じように作成できます。

use App\Models\Tag;
use App\Models\Video;

$videos = Video::factory()
            ->hasAttached(
                Tag::factory()->count(3),
                ['public' => true]
            )
            ->create();

もちろん、hasマジックメソッドを使用して、ポリモーフィックな「多対多」リレーションを作成することもできます。

$videos = Video::factory()
            ->hasTags(3, ['public' => true])
            ->create();

ファクトリ内でのリレーション定義

モデルファクトリ内でリレーションを定義するには、リレーションの外部キーへ新しいファクトリインスタンスを割り当てます。これは通常、belongsTomorphToリレーションなどの「逆」関係で行います。たとえば、投稿を作成時に新しいユーザーを作成する場合は、次のようにします。

use App\Models\User;

/**
 * モデルのデフォルト状態の定義
 *
 * @return array<string, mixed>
 */
public function definition(): array
{
    return [
        'user_id' => User::factory(),
        'title' => fake()->title(),
        'content' => fake()->paragraph(),
    ];
}

リレーションのカラムがそれを定義するファクトリに依存している場合は、属性にクロージャを割り当てることができます。クロージャは、ファクトリの評価済み属性配列を受け取ります。

/**
 * モデルのデフォルト状態の定義
 *
 * @return array<string, mixed>
 */
public function definition(): array
{
    return [
        'user_id' => User::factory(),
        'user_type' => function (array $attributes) {
            return User::find($attributes['user_id'])->type;
        },
        'title' => fake()->title(),
        'content' => fake()->paragraph(),
    ];
}

リレーションでの既存モデルの再利用

あるモデルが、他のモデルと共通のリレーションを持っている場合、ファクトリが生成するすべてのリレーションで、モデルの単一インスタンスが再利用されるように、recycleメソッドを使用してください。

例えば、AirlineFlightTicketモデルがあり、チケット(Ticket)は航空会社(Airline)とフライト(Flight)に所属し、フライトも航空会社に所属しているとします。チケットを作成する際には、チケットとフライトの両方に同じ航空会社を使いたいでしょうから、recycleメソッドへ航空会社のインスタンスを渡します。

Ticket::factory()
    ->recycle(Airline::factory()->create())
    ->create();

共通のユーザーやチームに所属するモデルがある場合、recycleメソッドが特に便利だと感じるでしょう。

recycleメソッドは、既存のモデルのコレクションを受け取ることもできます。コレクションをrecycleメソッドへ渡すと、ファクトリがそのタイプのモデルを必要とするときに、コレクションからランダムにモデルを選びます。

Ticket::factory()
    ->recycle($airlines)
    ->create();

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる