はじめに
アプリケーションをテストしたり、データベースにデータを投入したりする際に、データベースにいくつかのレコードを挿入する必要があるかもしれません。各カラムの値を手動で指定する代わりに、Laravelではモデルファクトリを使用して、各Eloquentモデルのデフォルト属性のセットを定義することができます。
ファクトリの書き方の例を見たい場合は、アプリケーション内のdatabase/factories/UserFactory.php
ファイルを確認してください。このファクトリはすべての新しいLaravelアプリケーションに含まれており、以下のファクトリ定義が含まれています:
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の基本ファクトリクラスを拡張し、definition
メソッドを定義するクラスです。definition
メソッドは、ファクトリを使用してモデルを作成する際に適用されるべきデフォルトの属性値のセットを返します。
fake
ヘルパーを介して、ファクトリはFaker PHPライブラリにアクセスでき、テストやデータ投入のためにさまざまな種類のランダムデータを便利に生成できます。
アプリケーションのFakerロケールは、faker_locale
オプションをconfig/app.php
設定ファイルで更新することで変更できます。
モデルファクトリの定義
ファクトリの生成
ファクトリを作成するには、make:factory
Artisanコマンドを実行します:
php artisan make:factory PostFactory
新しいファクトリクラスは、database/factories
ディレクトリに配置されます。
モデルとファクトリの発見規約
ファクトリを定義したら、Illuminate\Database\Eloquent\Factories\HasFactory
トレイトによってモデルに提供される静的factory
メソッドを使用して、そのモデルのファクトリインスタンスをインスタンス化できます。
``````php
use Database\Factories\Administration\FlightFactory;
/**
* Create a new factory instance for the model.
*/
protected static function newFactory()
{
return FlightFactory::new();
}
`
次に、対応するファクトリに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;
}
ファクトリの状態
状態操作メソッドを使用すると、モデルファクトリに適用できる離散的な変更を任意の組み合わせで定義できます。たとえば、Database\Factories\UserFactory
ファクトリには、デフォルトの属性値の1つを変更するsuspended
状態メソッドが含まれているかもしれません。
状態変換メソッドは通常、Laravelの基本ファクトリクラスによって提供されるstate
メソッドを呼び出します。state
メソッドは、ファクトリのために定義された生の属性の配列を受け取り、変更する属性の配列を返すクロージャを受け入れます:
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',
];
});
}
「削除済み」状態
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
{
/**
* Configure the model factory.
*/
public function configure(): static
{
return $this->afterMaking(function (User $user) {
// ...
})->afterCreating(function (User $user) {
// ...
});
}
// ...
}
状態メソッド内でファクトリコールバックを登録して、特定の状態に固有の追加タスクを実行することもできます:
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) {
// ...
});
}
ファクトリを使用したモデルの作成
モデルのインスタンス化
ファクトリを定義したら、Illuminate\Database\Eloquent\Factories\HasFactory
トレイトによってモデルに提供される静的factory
メソッドを使用して、そのモデルのファクトリインスタンスをインスタンス化できます。いくつかのモデルを作成する例を見てみましょう。まず、make
メソッドを使用して、モデルをデータベースに永続化せずに作成します:
use App\Models\User;
$user = User::factory()->make();
``````php
$users = User::factory()->count(3)->make();
`
状態の適用
モデルに任意のstateを適用することもできます。モデルに複数の状態変換を適用したい場合は、状態変換メソッドを直接呼び出すだけです:
$users = User::factory()->count(5)->suspended()->make();
属性のオーバーライド
モデルのデフォルト値の一部をオーバーライドしたい場合は、make
メソッドに値の配列を渡すことができます。指定された属性のみが置き換えられ、他の属性はファクトリによって指定されたデフォルト値のままになります:
$user = User::factory()->make([
'name' => 'Abigail Otwell',
]);
また、ファクトリインスタンス上でstate
メソッドを直接呼び出して、インライン状態変換を実行することもできます:
$user = User::factory()->state([
'name' => 'Abigail Otwell',
])->make();
マスアサインメント保護は、ファクトリを使用してモデルを作成する際に自動的に無効になります。
モデルの永続化
``````php
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
メソッドに属性の配列を渡します:
$user = User::factory()->create([
'name' => 'Abigail',
]);
シーケンス
時には、作成された各モデルの特定のモデル属性の値を交互に変更したい場合があります。これをシーケンスとして状態変換を定義することで実現できます。たとえば、admin
カラムの値をY
とN
の間で交互に変更したい場合があります:
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Sequence;
$users = User::factory()
->count(10)
->state(new Sequence(
['admin' => 'Y'],
['admin' => 'N'],
))
->create();
この例では、5人のユーザーがadmin
の値がY
のもので作成され、5人のユーザーがadmin
の値がN
のもので作成されます。
必要に応じて、シーケンス値としてクロージャを含めることができます。シーケンスが新しい値を必要とするたびにクロージャが呼び出されます:
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();
ファクトリの関係
多くの関係
次に、Laravelの流暢なファクトリメソッドを使用してEloquentモデルの関係を構築する方法を探ります。まず、アプリケーションにApp\Models\User
モデルとApp\Models\Post
モデルがあると仮定します。また、User
モデルがhasMany
関係をPost
と定義していると仮定します。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のマジックファクトリ関係メソッドを使用して関係を構築できます。たとえば、次の例では、関連モデルが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();
属する関係
ファクトリを使用して「多くの関係」を構築する方法を探ったので、関係の逆を探ります。for
メソッドを使用して、ファクトリが作成したモデルが属する親モデルを定義できます。たとえば、単一のユーザーに属する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のマジックファクトリ関係メソッドを使用して「属する」関係を定義できます。たとえば、次の例では、3つの投稿がuser
関係に属するべきであることを規約に基づいて判断します:
$posts = Post::factory()
->count(3)
->forUser([
'name' => 'Jessica Archer',
])
->create();
多対多の関係
多くの関係と同様に、「多対多」の関係は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のマジックファクトリ関係メソッドを使用して多対多の関係を定義できます。たとえば、次の例では、関連モデルがroles
関係メソッドを使用してUser
モデルで作成されるべきであることを規約に基づいて判断します:
$user = User::factory()
->hasRoles(1, [
'name' => 'Editor'
])
->create();
ポリモーフィック関係
ポリモーフィック関係もファクトリを使用して作成できます。ポリモーフィック「モーフ多」は、通常の「多くの」関係と同じ方法で作成されます。たとえば、App\Models\Post
モデルがmorphMany
モデルとApp\Models\Comment
モデルとの関係を持っている場合:
use App\Models\Post;
$post = Post::factory()->hasComments(3)->create();
モーフトゥ関係
マジックメソッドを使用してmorphTo
関係を作成することはできません。代わりに、for
メソッドを直接使用し、関係の名前を明示的に指定する必要があります。たとえば、Comment
モデルがcommentable
メソッドを持ち、morphTo
関係を定義していると仮定します。この場合、for
メソッドを直接使用して、単一の投稿に属する3つのコメントを作成できます:
$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
メソッドを使用してポリモーフィック「多対多」の関係を作成することもできます:
$videos = Video::factory()
->hasTags(3, ['public' => true])
->create();
ファクトリ内での関係の定義
モデルファクトリ内で関係を定義するには、通常、関係の外部キーに新しいファクトリインスタンスを割り当てます。これは通常、belongsTo
およびmorphTo
のような「逆」関係に対して行われます。たとえば、投稿を作成する際に新しいユーザーを作成したい場合は、次のようにします:
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(),
];
}
関係のカラムがそれを定義するファクトリに依存している場合は、属性にクロージャを割り当てることができます。クロージャは、ファクトリの評価された属性配列を受け取ります:
/**
* 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(),
];
}
関係のための既存モデルの再利用
他のモデルと共通の関係を持つモデルがある場合、recycle
メソッドを使用して、ファクトリによって作成されたすべての関係に対して関連モデルの単一インスタンスが再利用されることを保証できます。
たとえば、Airline
、Flight
、Ticket
モデルがあり、チケットが航空会社とフライトに属し、フライトも航空会社に属していると仮定します。チケットを作成する際には、チケットとフライトの両方に同じ航空会社を使用したいと思うでしょう。そのため、recycle
メソッドに航空会社のインスタンスを渡すことができます:
Ticket::factory()
->recycle(Airline::factory()->create())
->create();
共通のユーザーやチームに属するモデルがある場合、recycle
メソッドが特に便利です。
``````php
Ticket::factory()
->recycle($airlines)
->create();
`