Laravelで外部キー制約のない、親子同士のテーブルのファクトリを作成する


### 開発環境
- Windows 10 Pro(64bit)
- PHP 8.1.9 (cli) (built: Aug  2 2022 14:17:26) (ZTS Visual C++ 2019 x64)
- Laravel Framework 10.44.0

例えば、Usersテーブルがあって、ユーザーが何かしら記事を投稿するPostsテーブルがあるとすると、通常であれば、以下のように、外部キー制約で親子関係を結びます。
```sql
CREATE TABLE users(
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL,
  password VARCHAR(255) NOT NULL,
  created_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL
)DEFAULT CHARSET=utf8mb4;

CREATE TABLE posts(
  id INT AUTO_INCREMENT PRIMARY KEY,
  post TEXT NOT NULL,
  user_id INT NOT NULL,
  created_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL,
  FOREIGN KEY(user_id) REFERENCES users(id)
)DEFAULT CHARSET=utf8mb4;
```

しかし、updated_atにパーティションレンジをつけたいがために、外部キー制約が使えない場合、以下のようにしたいと思います。

```sql
CREATE TABLE users(
  id INT AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(255) NOT NULL,
  email VARCHAR(255) NOT NULL,
  password VARCHAR(255) NOT NULL,
  created_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL
)DEFAULT CHARSET=utf8mb4;

CREATE TABLE posts(
  id INT NOT NULL,
  post TEXT NOT NULL,
  user_id INT NOT NULL,
  INDEX(user_id),
  created_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL,
  PRIMARY KEY(id, updated_at)
)DEFAULT CHARSET=utf8mb4
PARTITION BY RANGE (UNIX_TIMESTAMP(updated_at)) (
  PARTITION p202403 VALUES LESS THAN (UNIX_TIMESTAMP('2024-04-01 00:00:00')),
  PARTITION p202404 VALUES LESS THAN ( UNIX_TIMESTAMP('2024-05-01 00:00:00') ),
  PARTITION p202405 VALUES LESS THAN ( UNIX_TIMESTAMP('2024-06-01 00:00:00') ),
  PARTITION p202406 VALUES LESS THAN ( UNIX_TIMESTAMP('2024-07-01 00:00:00') ),
  PARTITION p202407 VALUES LESS THAN ( UNIX_TIMESTAMP('2024-08-01 00:00:00') ),
  PARTITION p202408 VALUES LESS THAN ( UNIX_TIMESTAMP('2024-09-01 00:00:00') )
);
```

次にファクトリを作成します。今回は、configureメソッド内で、User1件作成するたびに、10件のタスクを作成しています。外部キー制約になっていれば、count(10)のように指定して同様のことをするのですが、できないため、以下のように記述しています。

```php:UserFactory.php
use Illuminate\Support\Facades\Hash;
use App\Models\User;
use App\Models\Task;

  public function definition(): array
  {
    return [
      'username' => fake()->name(),
      'email' => fake()->unique()->safeEmail(),
      'created_at' => now(),
      'updated_at' => now(),
      'password' => static::$password ??= Hash::make('password'),
    ];
  }

  public function configure(): self
  {
    return $this->afterCreating(function (User $user) {
      for ($i = 0; $i < 10; $i++) {
        Task::factory()->for($user)->create();
      }
    });
  }
```

```php:TaskFactory.php

use App\Models\User;
use Illuminate\Support\Facades\DB;

  public function definition(): array
  {
    //AUTO_INCREMENTがないため、手動でインクリメントする
    $latest_data = DB::table('tasks')->orderBy('id', 'desc')->first();
    $latest_id = 0;

    if (is_null($latest_data)) {
      $latest_id = 1;
    } else {
      $latest_id = $latest_data->id + 1;
    }

    return [
      'id' => $latest_id,
      'user_id' => User::factory(),
      'post' => fake()->text(),
      'created_at' => Carbon::now(),
      'updated_at' => Carbon::now()
    ];
  }
```

最後に呼び出すシーダーを書きます。以下のコードだと、ユーザーを50アカウント作成し、各アカウントが10件の投稿をします。

```php:UserSeeder.php
  public function run(): void
  {
    User::factory()
      ->count(50)->create();
  }
```

### 参考リンク
- [【PHPUnit】factoryのリレーション先のデータ作成はafterCreatingを使おう](https://qiita.com/kat0/items/e037bc97a67943585082)