Laravelでリレーション先のリレーションのデータを取得する時にHas Many Throughが便利っていう話

# 前書き
タイトルまんまですが、Laravelでリレーション先のリレーションのデータを取得する時にHas Many Throughが便利っていう話になります。
書き方とHas Many Throughを使わない場合との書き方の違い、簡単な活用方法等を書いていきます。

#### サービス概要
今回はユーザーが記事などを投稿して記事にコメントをできるようなサービスを考えてみます。

#### 環境
Laravel 10系

# テーブル設計

#### users（ユーザー）テーブル
| 論理名 | 物理名 | データ型 | 制約 |
| ---- | ---- | ---- | ---- |
| ユーザーID | id | UNSIGNED BIGINT | PRIMARY KEY, <br>AUTOINCREMENT |
| ユーザー名 | name | VARCHAR(255) | NOT NULL |
| 作成日時 | created_at | TIMESTAMP | NOT NULL |
| 更新日時 | updated_at | TIMESTAMP | |

#### posts（投稿）テーブル
| 論理名 | 物理名 | データ型 | 制約 |
| ---- | ---- | ---- | ---- |
| 投稿ID | id | UNSIGNED BIGINT | PRIMARY KEY, <br>AUTOINCREMENT |
| ユーザーID | user_id | UNSIGNED INTEGER| NOT NULL |
| タイトル | title | VARCHAR(255)| NOT NULL |
| 本文 | content | TEXT| NOT NULL |
| 作成日時 | created_at | TIMESTAMP | NOT NULL |
| 更新日時 | updated_at | TIMESTAMP | |

#### comments（コメント） テーブル
| 論理名 | 物理名 | データ型 | 制約 |
| ---- | ---- | ---- | ---- |
| コメントID | id | UNSIGNED BIGINT | PRIMARY KEY, <br>AUTOINCREMENT |
| ユーザーID | user_id | UNSIGNED INTEGER| NOT NULL |
| 投稿ID | post_id | UNSIGNED INTEGER| NOT NULL |
| コメント内容 | content | TEXT| NOT NULL |
| 作成日時 | created_at | TIMESTAMP | NOT NULL |
| 更新日時 | updated_at | TIMESTAMP | |

# リレーションの実装
### Has Many Throughの使い方
User.phpにリレーションを設定していきます。
```php:User.php
public function post_comments()
{
    return $this->hasManyThrough(Comment::class, Post::class);
}
```
今回はローカルキーや外部キーは特殊なパターンではないので第三引数以降を設定する必要がありません。

```php:User.php
public function post_comments()
{
    return $this->hasManyThrough(
        Comment::class,
        Post::class,
        'user_id', // postsテーブル（中間）の外部キー
        'post_id', // commentsテーブルの外部キー
        'id', // usersテーブルのローカルキー
        'id' // テーブルのローカルキー
    );
}
```
ローカルキーや外部キーをカスタムしている場合は第三引数以降を設定することを忘れないようにしてください。

詳しい設定は公式ドキュメントを見ていただければと思います。

https://readouble.com/laravel/10.x/ja/eloquent-relationships.html#has-many-through

# 使ってみる
書き方はシンプルです。
```php
$users = User::with('post_comments')->get();
```
これでユーザーの投稿に紐つくコメントが取得できます。

```php
$users = User::withCount('post_comments')->get();
```
こうすることでユーザーごとに投稿に紐つくコメントの総数が取得できます。

もちろん条件で絞り込みすることもできます。
以下では昨日から今日にかけて作られたユーザーごとに投稿に紐つくコメントの総数が取得できます。
```php
$today = Carbon::now();
$yesterday = Carbon::yesterday();
$users = User::withCount([
    'post_comments' => function ($q) use ($today, $yesterday) {
        $q->whereBetween('created_at', [$yesterday, $today]);
    }
]);
```

# Has Many Throughを使わない場合
### リレーションの設定
User.phpとPost.phpにリレーションを設定していきます。
```php:User.php
public function posts()
{
    return $this->hasMany(Post::class);
}
```
```php:Post.php
public function comments()
{
    return $this->hasMany(Comment::class);
}
```

### 使ってみる

```php
$users = User::with('posts.comments')->get();
```
これでユーザーの投稿とそれに紐つくコメントが取得できます。

投稿とコメント両方必要なパターンはこれで良いと思います。

# Has Many Throughはどういう時に使うのが良いか
私は中間のデータが必要ない時などに使用しております。
Has Many Throughを使うことで取得するデータの量も減りますし階層が浅くなるので取り扱いがしやすくなります。


# 結論
プロジェクトや機能によって必要な方を選択して実装をしてもらうのが良いと思います。

# 他の機能に関して
Has One Throughなど他にも便利な機能があるので興味がある方は公式ドキュメントを見ていただければと思います。

https://readouble.com/laravel/10.x/ja/eloquent-relationships.html#has-one-through

# リポジトリ
参考用にリポジトリを公開してあります。
https://github.com/Kent0129/laravel-relation-sample