Lang x Lang

Eloquent: Factories

Table of Contents

Introduction

あなたの application を testing したり、database に seeding を行う際には、いくつかのレコードを database に insert する必要があるかもしれません。それぞれの column の value を手動で指定する代わりに、Laravel ではEloquent modelsごとに一連の default attributes を定義することを許可しています。

factory の書き方の例を見るには、あなたの application 内のdatabase/factories/UserFactory.phpファイルをご覧ください。この factory は新たに作成された Laravel アプリケーションすべてに含まれており、次の factory の定義が含まれています。

namespace Database\Factories;

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

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
 */
class UserFactory extends Factory
{
    /**
     * The current password being used by the factory.
     */
    protected static ?string $password;

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        return [
            'name' => fake()->name(),
            'email' => fake()->unique()->safeEmail(),
            'email_verified_at' => now(),
            'password' => static::$password ??= Hash::make('password'),
            'remember_token' => Str::random(10),
        ];
    }

    /**
     * Indicate that the model's email address should be unverified.
     */
    public function unverified(): static
    {
        return $this->state(fn (array $attributes) => [
            'email_verified_at' => null,
        ]);
    }
}

ご覧の通り、最も基本的な形では、ファクトリは Laravel のベースの factory class を拡張するクラスであり、definition method を定義します。 definition method は、 factory を使用して model を作成する際に適用されるべき default の attribute values セットを返します。

fakeヘルパーを参照して、ファクトリはFaker PHP library にアクセスでき、これにより testing や seeding 用の様々な種類の random data を便利に生成することができます。

NOTE

アプリケーションの Faker locale を変更するには、config/app.php設定ファイルのfaker_localeオプションを更新します。

Defining Model Factories

ファクトリーの生成

make:factory Artisan commandを実行して、 factory を作成します:

php artisan make:factory PostFactory

新しい factory class はあなたの database/factories ディレクトリに配置されます。

Model と Factory の発見規約

model ーを定義したら、Illuminate\Database\Eloquent\Factories\HasFactory トレイトが提供する静的な factory method を使って、その models のための factory ー インスタンスを作成することができます。

HasFactoryトレイトのFactory method は、トレイトが割り当てられている model の適切な factory を決定するための規約を使用します。特に、 method は、Database\Factories 名前空間内で model の名前と一致し、factoryという接尾辞が付いた class 名を持つ factory を探します。これらの規約が特定の application または factory に適用されない場合は、newFactory method を上書きして、モデルの対応する factory のインスタンスを直接返すように model を設定することができます。

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

/**
 * Create a new factory instance for the model.
 */
protected static function newFactory(): Factory
{
    return FlightFactory::new();
}

次に、対応する factory にmodelプロパティを定義します:

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

class FlightFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var class-string<\Illuminate\Database\Eloquent\Model>
     */
    protected $model = Flight::class;
}

Factory states

State 操作方法により、model ファクトリに任意の組み合わせで適用できる離散的な変更を定義することができます。たとえば、あなたのDatabase\Factories\UserFactory factory には、その default 属性 values の 1 つを変更するsuspended ステート methods が含まれるかもしれません。

State 変換メソッドは通常、Laravel の基本 factory class によって提供されるstate method を呼び出します。state method はクロージャを受け取り、そのクロージャは factory に定義された生の attributes の array を受け取り、変更する attributes の array を返すべきです:

use Illuminate\Database\Eloquent\Factories\Factory;

/**
 * Indicate that the user is suspended.
 */
public function suspended(): Factory
{
    return $this->state(function (array $attributes) {
        return [
            'account_status' => 'suspended',
        ];
    });
}

"Trashed" State

あなたの Eloquent model がソフト削除可能な場合、作成された model がすでに「ソフト削除」されたことを示すために、ビルトインのtrashed state method を呼び出すことができます。すべてのファクトリーに自動的に利用可能なので、trashed state を手動で定義する必要はありません。

use App\Models\User;

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

Factory コールバック

Factory コールバックは、afterMakingおよびafterCreatingメソッドを使用して登録され、作成または model を作成した後に追加のタスクを実行することができます。これらのコールバックを定義することで、configure method を factory class に register する必要があります。この method は、 factory がインスタンス化されたときに Laravel によって自動的に呼び出されます。

namespace Database\Factories;

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

class UserFactory extends Factory
{
    /**
     * Configure the model factory.
     */
    public function configure(): static
    {
        return $this->afterMaking(function (User $user) {
            // ...
        })->afterCreating(function (User $user) {
            // ...
        });
    }

    // ...
}

また、特定の state に固有の追加タスクを実行するために、 state メソッド内で register factory コールバックを登録することもできます:

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

/**
 * Indicate that the user is suspended.
 */
public function suspended(): Factory
{
    return $this->state(function (array $attributes) {
        return [
            'account_status' => 'suspended',
        ];
    })->afterMaking(function (User $user) {
        // ...
    })->afterCreating(function (User $user) {
        // ...
    });
}

Creating Models Using Factories

Models のインスタンス化

あなたがあなたの factory を定義したら、Illuminate\Database\Eloquent\Factories\HasFactory トレイトがあなたの models に提供する static factory method を使ってその models のための factory ーインスタンスを生成することができます。 models の作成例をいくつか見てみましょう。まず、make method を使用して database に保存せずに models を作成します:

use App\Models\User;

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

count method を使用して、多数の models の collection を作成することができます:

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

ステートの適用

あなたはまた、あなたのstatesを models に適用することもできます。もし、複数の state 変換を models に適用したい場合は、単に state 変換 methods を直接呼び出すことができます。

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

Attributes のオーバーライド

もしあなたが自分の models の一部の default values を上書きしたい場合、make method に values の array を渡すことができます。指定された attributes だけが置き換えられ、残りの attributes は factory で指定された default values のままになります:

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

あるいは、state method は、インラインの state 変換を実行するために、直接 factory インスタンスに呼び出すことができます:

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

NOTE

マスアサインメント保護は、ファクトリを使用して models を作成するときに自動的に無効になります。

Models の永続化

create method は、 model のインスタンスを生成し、Eloquent のsave method を使用してそれらを database に永続化します:

use App\Models\User;

// Create a single App\Models\User instance...
$user = User::factory()->create();

// Create three App\Models\User instances...
$users = User::factory()->count(3)->create();

create method を使用して attributes の array を渡すことで、工場の default model attributes を上書きすることができます。

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

Sequences

時折、作成されるすぐ model ごとに特定の model attribute の value を交互に切り替えたいと思うことがあるかもしれません。これは、 sequence として state 変換を定義することで達成できます。例えば、作成されるすぐ user ごとに admin column の value を YN に交互に切り替えたい場合があります。

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

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

この例では、5 人の users がadminの value がYで作成され、5 人の users がadminの value がNで作成されます。

必要に応じて、クロージャを sequence value として含めることができます。このクロージャは、 sequence が新しい value を必要とするたびに呼び出されます:

use Illuminate\Database\Eloquent\Factories\Sequence;

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

sequence クロージャの中では、クロージャに注入された sequence インスタンスの$indexまたは$countプロパティにアクセスできます。$indexプロパティは、これまでに sequence が何回反復されたかを示す数値を含んでいます。一方、$countプロパティは sequence が呼び出される総回数を含んでいます:

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

便宜上、シーケンスはsequence method を使用して適用することもできます。これは単純に内部でstate method を呼び出します。sequence method は閉鎖またはシーケンス化された attributes の配列を受け入れます。

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

Factory Relationships

Has Many Relationships

次に、Laravel の流暢な factory メソッドを使用して、 Eloquent model の関連付けを構築する方法を探ってみましょう。まず、App\Models\User model とApp\Models\Post model があると仮定して、我々の application があると仮定しましょう。また、User model がPosthasManyの関連性を定義していると仮定しましょう。Laravel の factories が提供するhas method を使用して、三つの posts を持つ user を作成することができます。has method は、 factory インスタンスを受け入れます:

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

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

慣例により、Post model をhas method に渡すと、 Laravel はUser model にposts method が存在し、その関係性を定義していると仮定します。必要であれば、操作したい関係性の名前を明示的に指定することができます:

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

もちろん、関連する models に対して state の操作を行うことができます。さらに、親の model へのアクセスが必要な state の変更を行う場合、クロージャベースの state 変換を渡すことができます。

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

マジックメソッドの使用

便宜上、Laravel の魔法の factory リレーションシップメソッドを使用して build 関係を構築することができます。例えば、以下の例では、関連する models がUser model のpostsリレーションシップ method を通じて作成されるべきであるという慣習を使用しています。

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

マジックメソッドを使って factory 関係を作成するときには、関連する models で上書きするための attributes の array を渡すことができます:

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

あなたの state が親の model へのアクセスを必要とする場合、クロージャベースの state 変換を提供してもよいです:

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

所属関係

これで、ファクトリを使用して"has many"関係を build する方法を探索しましたので、関係の逆を探索しましょう。for method は、 factory が作成した models が属する親の model を定義するために使用できます。例えば、 single user に所属する三つのApp\Models\Post model インスタンスを作成することができます。

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

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

すでに親の model インスタンスがあり、それが作成する models と関連付ける必要がある場合、 model インスタンスをfor method に渡すことができます。

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

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

マジックメソッドの使用

便宜上、 factory のマジックリレーションシップメソッドを使って"belongs to"関係を定義することができます。たとえば、次の例は、三つの posts がPost model 上のuserリレーションシップに所属すべきであるということを規約によって決定します:

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

多対多の関係性

has many relationships と同様に、"many to many" の関係性は has method を使用して作成することができます。

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

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

Pivot テーブルの Attributes

models を link するピボット/中間テーブルに設定すべき属性を定義する必要がある場合、hasAttached method を使用できます。この method は、2 つ目の引数としてピボットテーブルの属性名と values の array を受け付けます。

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

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

あなたの state 変更が関連する model へのアクセスを必要とする場合、クロージャに基づく state 変換を提供できます:

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

すでにあなたが作成している models に関連付けたい model のインスタンスがある場合、それらの model インスタンスを hasAttached method に渡すことができます。この例では、同じ 3 つのロールがすべての 3 つの users に関連付けられます:

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

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

マジックメソッドの使用

便宜上、多対多の関係を定義するために Laravel のマジック factory 関係メソッドを使用することができます。例えば、以下の例では、関連する models がUser model 上のroles関係 method を通じて作成されるべきであるという規約を用います:

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

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

Polymorphic relationshipsは、factory を使用して作成することもできます。Polymorphic の"morph many"関係は、典 types 的な"has many"関係と同じ方法で作成されます。例えば、App\Models\Postの model がmorphManyの関係をApp\Models\Commentの model と持っている場合:

use App\Models\Post;

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

関係性への変形

マジックメソッドは、morphTo関係を作成するためには使用できません。代わりに、for method を直接使用し、関係の名前を明示的に提供する必要があります。例えば、Comment model がcommentable method を持ち、morphTo関係を定義していると想像してみてください。この状況では、for method を直接使って、 single 投稿に属する三つの comments を作成することができます:

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

多態性多対多関係

多型の「多対多」(morphToMany / morphedByMany) 関係は、非多型の「多対多」関係と同様に作成することができます。

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

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

もちろん、魔法の has method は、多対多のポリモーフィックな関係を作成するためにも使用することができます:

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

ファクトリー内での関係性の定義

model factory "内の関係性を定義するためには、通常、新たな" factory "インスタンスを関係性の外部キーに割り当てます。これは通常、belongsTomorphToのような"逆"の関係性に対して行われます。例えば、投稿を作成する際に新しい" user "を作成したい場合、以下のようにすることができます:

use App\Models\User;

/**
 * Define the model's default state.
 *
 * @return array<string, mixed>
 */
public function definition(): array
{
    return [
        'user_id' => User::factory(),
        'title' => fake()->title(),
        'content' => fake()->paragraph(),
    ];
}

もしリレーションシップのカラムがそれを定義する factory に依存している場合、クロージャを attribute に割り当てることができます。クロージャはファクトリーの評価された attribute array を受け取ります:

/**
 * Define the model's default state.
 *
 * @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(),
    ];
}

既存の Model をリレーションシップのためにリサイクルする

もし、他の model と共通の関係を共有する model がある場合、recycle method を使用して、factory によって作成されたすべての関係に対して関連する model のシングルインスタンスがリサイクルされることを保証することができます。

例えば、AirlineFlight、そしてTicketという models を持っていると想像してみてください。チケットは航空会社とフライトに所属し、フライトも航空会社に所属します。チケットを作成する際、チケットとフライトの両方で同じ航空会社を使いたいと考えるでしょうから、航空会社のインスタンスをrecycleという method に path できます。

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

recycle method は、共通の user またはチームに所属する models がある場合に特に役立つかもしれません。

recycleの method は、既存の models の collection も受け入れます。recycleの method に collection が提供された場合、 factory がその type の model を必要とするときに、 collection から random model が選ばれます。

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

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use