環境
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されています。
またリレーションの設定もこの段階で完了しておく必要があります。
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); }}class Post extends Model{ use HasFactory
public function user(): BelongsTo { return $this->belongsTo(User::class); }
public function tags(): BelongsToMany { return $this->belongsToMany(Tag::class); }}class Tag extends Model{ use HasFactory;
public function posts(): BelongsToMany { return $this->belongsToMany(Post::class); }}ファクトリ
いよいよファクトリファイルを作りますが、ファクトリもそんなに悩むところはありません。
UserFactory.phpは基本デフォルトのままですが、created_at,updated_atを入力しないとコマンドを実行した日時になるので、もしランダムに入れたいときはdateTimeBetweenを使うといいかもしれないです。
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()を指定します。
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テーブル。中間テーブルはモデルと同様に必要ありません。
public function definition(){ return [ 'slug' => $this->faker->unique()->slug(), 'name' => $this->faker->unique()->word(), ];}これで準備は完了です。
シーダファイルの作成
ここがらが本題です。
作成したファクトリでどのようなデータが作成されるか確認していきましょう。
DatabaseSeeder.phpにfactoryメソッドを記述していきます。
最初にPostモデルのfactoryを単独で実行してみます。
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))) );もっとスマートな方法がありそうだけどわかりませんでした。以上。



