【Laravel】morphToによるリレーションにおけるEager Loading

## 前提
* Documentを見てて、「こんなのあるんだ！」と知ったのでサクッとアウトプット
* 私の実務経験歴：1か月
* 基本的なLaravelの使い方は割愛

## 開発環境
* Windows 10 Home
* Laravel 8.x
* PHP 7.x

## やりたいこと
* 以下二つのリレーションを設定する
  * Order→User→UserAttribute
  * Order→Guestuser
* ↓こんな感じのeager loadingが確認できることが目標

### ユースケース
* 「登録ユーザー」（User）と「ゲストユーザー」（GuestUser）が共存するECサイト
* 同じ画面で同じ項目（例えば、年齢（age）など）を取得し、出力する

## 参考
Laravel公式ドキュメント「morphToリレーションのネストしたEagerロード」
https://readouble.com/laravel/8.x/ja/eloquent-relationships.html#nested-eager-loading-morphto-relationships

## 前提
### モデル構成
* 必要最低限だけ記入

#### Order Model
* テーブル名：orders

| カラム名 | 型 |  備考 |
|:-:|:-:|:-:|
| id  |  int |  PK |
| orderable_id  | int  | FK |
| name  | string  |   |

#### User
* テーブル名：users

| カラム名 | 型 | 備考 |
|:-:|:-:|:-:|
| id  |  int |  PK |
| name  | string  |   |

#### GuestUser
* テーブル名：guest_users

| カラム名 | 型 | 備考 |
|:-:|:-:|:-:|
| id  |  int |  PK |
| name  | string  |   |
| age  | string  |   |

#### UserAttribute
* テーブル名：user_attributes

| カラム名 | 型 | 備考 |
|:-:|:-:|:-:|
| id  |  int |  PK |
| user_id  |  int |  FK |
| age  | string  |   |


### Route
* なんでもいいので、GETメソッドのルートを決めます。

```php
Route::get('/hello',[HelloController::class, 'index']);
```
### Controller
* 最低限のuseしかしていないです。

```php

<?php

namespace App\Http\Controllers;

use ...
use Illuminate\Database\Eloquent\Relations\MorphTo;
use App\Models\Order;

class HelloController extends Controller
{
    public function index(Request $request)
    {
        $orders = Order::query()
        ->with(['orderable' => function (MorphTo $morphTo) {
            $morphTo->morphWith([
                User::class => ['userAttribute'],
            ]);
        }])->get();

    }

```

### View
* dump結果だけなので、割愛

### Model
#### Order

```php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    use HasFactory;
    // UserとGuestUserのポリモーフィック
    public function orderable(){
        return $this->morphTo();
    }
}
```

#### User

```php
class User extends Authenticatable
{
    ...
    // UserAttributeの値を取得
    public function userAttribute(){
        return $this->hasOne(UserAttribute::class);
    }
}
```

#### GuestUser, UserAttribute
特に記載する必要なし

### 動作確認
* `http://localhost/プロジェクト名(/public)/hello`にアクセスしデバッグバーでクエリを確認
  * クエリが `select * from 'user_attribute' where 'user_attribute'.'user_id' = in (1, 2, 3, ...);` とin句を使って取得できていれば成功
  * クエリが以下のようにin句ナシで取得できている場合はEagerloadingができておらず、N+1問題が発生している
    *  `select * from 'user_attribute' where 'user_attribute'.'user_id' = 1;`
    *  `select * from 'user_attribute' where 'user_attribute'.'user_id' = 2;`
    *  `select * from 'user_attribute' where 'user_attribute'.'user_id' = 3;`


### ちなみに…
* 最初僕が試したのは以下参考に示したネストしたEager Loading

https://readouble.com/laravel/8.x/ja/eloquent-relationships.html#nested-eager-loading

* Controllerのwithメソッドをこうしてみた

```php
$orders = Order::with('orderable.userAttribute');
```

* 「めっちゃシンプルにEager Loadingできんじゃん！」とテンション上がりましたが、

`Undefined method 'userAttribute' at [App\Models\GuestUser]`
※訳「Guestユーザーに`userAttribute`なんてねーよ！バカ！」

と怒られました。
GuestUserにはuserAttributeなんてメソッドは存在しない。そもそも必要ないのだ。浅はかでした。

### 参考

morphToのリレーションにおけるEagerloadingはLaravel 5.8 ~の産物だということを始めて知りました。

https://laravel-news.com/laravel-5-8-22