LaravelのFactoryの理解があまかった件について

# はじめに
久しぶりの投稿でQiitaCLIのコマンドを忘れてしまったちびまろです。

早速ですがみなさん、Laravelは好きですか？

僕は好きです！大好きです！

プライベートで少しずつ勉強しているのでまだまだひよっこですが、新しく機能について理解が深まるともっともっとと気持ちが昂ります！

理由？分かりません。好きなもんは好きなんです！

題名にあるとおり、今回はLaravelの**Factory**について書かせていただければと思います。

私自身何となく「テストデータを大量に作るやつ」くらいのイメージで使ってましたが、ちゃんと理解して使おうと思い立ったので調べてみることにしました。

## Factoryとは
<font color="red">**データベースにテスト用のダミーデータを簡単に作成するための設計書**</font>です。

勉強している最中にイメージ的にmigrationと似ているなと感じました。

Factoryはテストや開発中に大量のデータを扱いたいときに便利な機能です。
本物のデータを用意しなくてもFactoryとFaker(PHPライブラリ)を組み合わせることで簡単にランダムなデータを生成することができます。

この機能を使うことで手動で何十、何百ものデータを作成する手間を省くことができます。

めちゃくちゃ便利...!

Laravelの中にdatabaseディレクトリがあり、その中にFactoryクラスが存在します。

```bash
database
├── database.sqlite
├── factories
│   └── UserFactory.php
├── migrations
│   ├── 0001_01_01_000000_create_users_table.php
│   ├── 0001_01_01_000001_create_cache_table.php
│   └── 0001_01_01_000002_create_jobs_table.php
└── seeders
    └── DatabaseSeeder.php
```

### Factoryの作成方法
Factoryを新たに作成することも可能です。
Laravelには**artisanコマンド**と呼ばれるLaravel独自のコマンドがあります。
こちらのコマンドで新しいFactoryを作成できます。
```
php artisan make:factory factoryName
```
このコマンドを実行すると、`database/factories`ディレクトリ内に新しいFactoryクラスが生成されます。
factoryNameで作成するFactoryクラスの名前を指定できます。

**例**
```bash
php artisan make:factory PostFactory
```
このコマンドを実行すると、`database/factories/PostFactory.php`が生成されます。

```bash
database
├── factories
│   ├── PostFactory.php
│   └── UserFactory.php
... 省略
```

このファイル内でモデルのデフォルトの属性値を定義します。

### Factoryの中身について
今回はLaravelインストール時にデフォルトで作成される`UserFactory.php`の中身を見ていきましょう。

```php:UserFactory.php
<?php

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,
        ]);
    }
}
```
#### 各コードの意味
ズラーっとコードがたくさんあると少々混乱するかもしれませんが、１つずつに絞ってみていくとそれほど難しくもありません。
すぐ理解できなくてもそんなもんです！

ということで１つずつ説明していきます。

#### `protected static ?string $password;`

プログラミングしてる人ならよく見るコードの形ですね！

「最もprotectedで、最もstaticで、そして最もstringな宣言でいかせていただきます。」(何言ってんだ)

すみません、ふざけすぎました。

##### `protected`
プロパティやメソッド、定数などの**アクセス権の制御を示すキーワード**です。
public、protected、privateのいずれかを指定することで定義が可能です。

`protected`を宣言されたものは、そのクラス自身やそのクラスを継承したクラス、親クラスからのみアクセスが可能です。
それ以外の場所からは直接アクセスすることはできません。
つまり、今回でいうと`UserFactory.php`の中でのみ、この`$password`に値を代入したり、値を参照したりすることが可能です。

##### `static`
クラスプロパティ、またはメソッドに対して`static`を宣言すると、クラスのインスタンス化を行う必要がなくなります。

インスタンスを使用しなくても、クラスを直接指定することで変数やインスタンスを呼び出せるようにできます。

どうやら擬似変数である`$this`については、staticを宣言されたメソッドの内部から利用することが出来ないらしいです。

##### `?string`

型宣言で`string`は見たことありますが、先頭にある`?`って何ですかね。

[公式ドキュメント](https://www.php.net/manual/ja/migration71.new-features.php)に記載がありました。

パラメータや戻り値の型宣言をする際に、型の前に`?`をつけるとnullableであることを指定できる。

クエスチョンマークの後ろで指定した型だけでなく、nullも渡せるようになります。

何でもそうですが、便利〜！と安易に考えて使用する場面をミスるとバグに繋がりかねない機能だなと思いました。
使用する際は注意したいですね！！

### definitionメソッド
メソッドのすぐ上にコメントが書いてありますね。

>Define the model's default state.

日本語訳は「モデルのデフォルト状態を定義する。」です。

これはFactoryで生成されるユーザーの「初期データ」を設定するためのメソッドです。

このメソッドは下記のようなコードを実行すると呼び出され、生成するユーザーのデフォルトの属性を指定します。
```PHP
User::factory()->make()
User::factory()->create()
```

Userモデルと紐づいており、下記コードのreturnで指定されている`name`や`email`、`password`などはusersテーブルのカラムと紐づいています。

```php:UserFactory.php
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),
    ];
}
```

`fake()->???()`とすることで、PHPのライブラリであるFakerを使ってダミーデータを生成して！と指示することができます。


#### name
ユーザーの名前が登録されるカラム。
`fake()->name()`を使うことでランダムな名前が生成されます。

#### email
ユーザーのメールアドレスが登録されるカラム。

**ランダムで一意なメールアドレスを生成**
```PHP
fake()->unique()->safeEmail()
```

---
ではここで`UserFactory.php`の中にある`password`で何をしているのかを見ていきましょう。


```PHP
'password' => static::$password ??= Hash::make('password'),
```

初めてUserFactoryが実行される時、左側の`static::$password`の状態はnullです。

`static::$password`がnullの場合に`Hash::make('password')`が実行されます。

**1回目の実行**
`Hash::make('password')`が実行されます。

**2回目以降**
`static::$password`に格納されたデータが再利用されます。


ではいつ格納されるのか！！！！
ここで注目すべきものが[`??=`](https://www.php.net/manual/ja/language.operators.assignment.php#language.operators.assignment.other)です。

これは **null合体代入演算子** と呼ばれる機能で左辺の変数がnullの場合に限り、右辺の値を代入することができる演算子です。


**処理の流れ**
1. 1回目の実行では、`static::$password`がnullなので、`Hash...`が実行されます
2. `Hash::make('password')`が実行されると同時に`??=`によって、`static::$password`に結果が代入されます
    <INS><STRONG>※`static::$password`はstaticプロパティであるため、このクラス全体から使えます</STRONG></INS>
3.  2回目以降の実行では、`static::$password`に値が入っているため、`??=`による代入は実行されません。1回目の実行で`static::$password`に代入されたハッシュ化パスワードがをのまま使われます



### unverifiedメソッド
ECサイトとかのユーザーを新規登録するとき、入力したメールアドレス宛に確認メールが届きますよね？

このメソッドはそれ関連で、ユーザーがメールを未確認の状態で作成されるように設定します。
確認メールを承認していないユーザーが必要な場合もあるため、その際にこの`unverified`メソッドを使います。

下記ロジックのようにこの`unverified`メソッドを使うことができます。
```php
$user = User::factory()->unverified()->create()
```

上記が実行されると、usersテーブルの`email_verified_at`にnullが設定されたテストデータが登録されます。

## まとめ
- Fakerはダミーデータ作成のための[PHPライブラリ](https://fakerphp.org/)
- Factoryはダミーデータ作成のための設計書(定義書)
- 基本的には`definition()`でテーブルに対するテストデータを定義する
- [`Null合体演算子`](https://www.php.net/manual/ja/language.operators.assignment.php#language.operators.assignment.other) と呼ばれる演算子がある

なんとなく**テストデータを作成するためのデータ**と認識していたレベルでしたが、今回改めて調べた結果理解度がかなりアップしましたね！
他にもなんとなくで使用している機能があるのでどんどん調べていこうと思いました！
ごっつ楽しくなりそうですねww

ではみなさん！今日もプログラミングを楽しんでいきましょう！！