環境
PHP 8.1
Laravel 9.19
テーブル構造
最初にマイグレーションファイルを見ていきましょう。
usersはデフォルトのまま。
Schema::create('users', static function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
usersテーブルと一対多で関連付けされているpostsテーブル。
Schema::create('posts', static function (Blueprint $table) {
$table->id();
$table->string('title', 255);
$table->text('body');
$table->foreignId('user_id');
$table->timestamps();
});
postsテーブルと多対多の関連付けされているtagsテーブル。
Schema::create('tags', static function (Blueprint $table) {
$table->id();
$table->string('slug', 100);
$table->string('name', 100);
$table->timestamps();
});
postsテーブルとtagsテーブルの中間テーブルになるpost_tag
中間テーブルにもメモ的なカラムがあります。
Schema::create('post_tag', static function (Blueprint $table) {
$table->id();
$table->foreignId('post_id');
$table->foreignId('tag_id');
$table->string('memo')->nullable();
});
こんな感じでそれぞれのテーブルにFactoryでデータを作成していきます。
モデル
Factoryを使用するにはモデルが必要です。
Factoryを使用するモデルにはHasFactoryをuseする必要があります。
makeコマンドで作成しているならデフォルトでuseされています。
またリレーションの設定もこの段階で完了しておく必要があります。
app/Models/User.php
use Illuminate\Database\Eloquent\Factories\HasFactory;
class User extends Authenticatable implements MustVerifyEmail
{
use HasApiTokens, HasFactory, Notifiable;
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
}
app/Models/Post.php
class Post extends Model
{
use HasFactory
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
}
app/Models/Tag.php
class Tag extends Model
{
use HasFactory;
public function posts(): BelongsToMany
{
return $this->belongsToMany(Post::class);
}
}
ファクトリ
いよいよファクトリファイルを作りますが、ファクトリもそんなに悩むところはありません。
UserFactory.phpは基本デフォルトのままですが、created_at,updated_atを入力しないとコマンドを実行した日時になるので、もしランダムに入れたいときはdateTimeBetweenを使うといいかもしれないです。
database/factories/UserFactory.php
public function definition()
{
$date = $this->faker->dateTimeBetween('-1year');
return [
'name' => $this->faker->unique()->userName(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
'remember_token' => Str::random(10),
'created_at' => $date,
'updated_at' => $date,
];
}
次はusersテーブルと関連を持っているpostsです。
user_idにUser::factory()を指定します。
database/factories/PostFactory.php
public function definition()
{
$date = $this->faker->dateTimeBetween('-1year');
return [
'title' => $this->faker->realText(random_int(30, 250)),
'body' => $this->faker->realText(random_int(200, 800)),
'user_id' => User::factory(),
'created_at' => $date,
'updated_at' => $date,
];
}
最後にtagsテーブル。中間テーブルはモデルと同様に必要ありません。
database/factories/TagFactory.php
public function definition()
{
return [
'slug' => $this->faker->unique()->slug(),
'name' => $this->faker->unique()->word(),
];
}
これで準備は完了です。
シーダファイルの作成
ここがらが本題です。
作成したファクトリでどのようなデータが作成されるか確認していきましょう。
DatabaseSeeder.phpにfactoryメソッドを記述していきます。
最初にPostモデルのfactoryを単独で実行してみます。
database/seeders/DatabaseSeeder.php
public function run()
{
Post::factory(10)->create();
}
初回はmigrate実行時にseedオプションを付与して実行します。
$ php artisan migrate:fresh --seed
すでにテーブルがある場合はdb:seedを実行します。
$ php artisan db:seed
ファクトリファイルでUser::factory()を指定したので、postsと同じ数だけusersにもレコードが作成され関連付けされたと思います。
ただこれだと同じ数のデータなので一対一の関係ですね。
一対多のデータ作成
一であるUserを先に作成します。
User::factory(3) ->hasPosts(3) ->create();
ただこれだとUserが持っているPostの数が固定になります。
ランダムな数で関連付けしたい場合はUserデータを作成した後、Postのfactoryでrecycleを実行します。
$users = User::factory(5)->create(); Post::factory(50)->recycle($users)->create();
これでpostsテーブルのuser_idカラムに、あらかじめ作成したusersのランダムなidが入り一対多のデータになります。
多対多のデータ作英
多対多はいろいろパターンがあるのですが、最初はPostレコードの数だけTagも作成する方法です。
ひとつのPostに対して3つずつTagを作るのは下記のようにします。
Post::factory(50) ->hasTags(3) ->create();
次にあらかじめTagを作って関連付けする方法です。
$tags = Tag::factory(3)->create(); Post::factory(50) ->hasAttached($tags) ->create();
中間テーブルのカラムに入れる場合。
Post::factory(50) ->hasAttached( $tags, fn () => ['memo' => fake()->realText(random_int(10, 30))] ) ->create();
関連付けるTagの数もランダムにしたい場合。
Post::factory(50) ->create() ->each(fn ($post) => $post ->tags() ->attach($tags->random(random_int(1, 3))) );
最後に中間テーブルのカラムもランダムで入れたい場合。
Post::factory(50) ->create() ->each(fn ($post) => array_map(static fn ($value) => $post ->tags() ->attach( $tags->random(), ['memo' => fake()->realText(random_int(30, 50))] ), range(1, random_int(1, 3))) );
もっとスマートな方法がありそうだけどわかりませんでした。以上。

