#【 Eagerロード 】コメント&コメントに対する返信について、それぞれユーザー名を別テーブルから取得したい
2019年11月から、Laravel歴数ヶ月の初心者が投稿型ナレッジベースのコミュニティサイトを作るというチャレンジ中。作りたいアプリケーション→機能を因数分解→ググる→先人の轍をたどる(写経する)→ぬかるみにはまる→エラー解消の神を探す→解決を繰り返す日々( ·ㅂ·)و 。備忘録として、Qiitaに投稿しています。
##今回はハマったポイントを共有
投稿に対し、コメントがつけられ、さらにそのコメントに対して返信ができるような機能を実装しようとしていた時のこと。
一つの投稿に紐づいているコメントとそれに対する返信を全て取得し、viewファイルへ渡してあげたい。その際、コメントしたユーザーと返信したユーザーについて、それぞれ名前をusers_tableから引っ張ってきたいと思ったが、コメントのユーザー名まではうまくいったけど、返信したユーザー名を取得するところで詰まった。
##前提
テーブルは以下のような設計になっており、投稿にコメントを書くことができ、一つ一つのコメントに対して返信ができるような構造。コメント、返信のいずれもuser_idでusers_tableと紐づいています。
posts_table
- id
- title
- body
- user_id
comments_table
- id
- post_id(posts_tableのidに紐付き)
- body
- user_id(users_tableのidに紐付き)
replies_table
- id
- comment_id(comments_tableのidに紐付き)
- body
- user_id(users_tableのidに紐付き)
users_table
- id
- name
##リレーションの設定
リレーションは、以下のような関係性になっています。
- 投稿は複数のコメントを持ち、コメントは一つの投稿に従属
- コメントは複数のリプライを持ち、リプライは一つのコメントに従属
- ユーザーは複数の投稿・コメント・リプライを持ち、投稿・コメント・リプライのいずれも一人の投稿者に従属する
それぞれ、モデルに以下のように設定しています。
*余談・・・テーブルデザイン・リレーションについては、きっともっと賢い書き方があると思います。どなたかいい方法をご存知でしたら教えてくれると嬉しいです(、._. )、
#Post.php
class Post extends Model
{
// 投稿は複数のコメントを持ち、コメントは一つの投稿に従属
public function comments()
{
return $this->hasMany('App\Comment');
}
// 投稿は一人の投稿者に従属
public function user()
{
return $this->belongsTo('App\User');
}
}
#Comment.php
class Comment extends Model
{
// 投稿は複数のコメントを持ち、コメントは一つの投稿に従属
public function post()
{
return $this->belongsTo('App\Post');
}
// コメントは一人の投稿者に従属
public function user()
{
return $this->belongsTo('App\User');
}
// コメントは複数のリプライを持ち、リプライは一つのコメントに従属
public function replies()
{
return $this->hasMany('App\Reply');
}
}
#Reply.php
class Reply extends Model
{
// リプライは一人の投稿者に従属
public function user()
{
return $this->belongsTo('App\User');
}
// コメントは複数のリプライを持ち、リプライは一つのコメントに従属
public function comment()
{
return $this->belongsTo('App\Comment');
}
}
#User.php
class User extends Model
{
// 投稿者は複数の投稿を持つ。
public function posts()
{
return $this->hasMany('App\Post');
}
// 投稿者は複数のコメントを持つ。
public function comments()
{
return $this->hasMany('App\Comment');
}
// 投稿者は複数のリプライを持つ。
public function replies()
{
return $this->hasMany('App\Reply');
}
}
####コントローラー
投稿のid($post_id)を指定し、DBからデータを取得して行きましょう。
親のモデルに対するクエリと同時にリレーションを取得してしまう**「Eagerロード」**という、スッキリした書き方があるそうです。
Eagerロードについては、laravelでwithを使ってSQLの読み込み回数を減らすに詳しく書いてあります。
#PostsController.php
public function show($post_id)
{
$comments = Comment::with(['user', 'replies', 'replies.user'])
->where('comments.post_id', $post_id)
->get();
return view('posts.show', [
'comments' => $comments,
]);
}
with後の丸括弧内に入っている3つが、取得したいリレーション先(テーブル)になります。
'user'(←Comment.phpモデルの"public function user()"で指定したメソッド名)
'replies'(←Comment.phpモデルの"public function replies()"で指定したメソッド名)
'replies.user'(←Reply.phpモデルの"public function user()"で指定したメソッド名)
comment->userとcomment->repliesは直接の従属関係にありますが、comment->replies->userへと、祖孫関係にあるところまでリーチできるという便利さに感動!!٩(๑′∀ ‵๑)۶•¨•.¸¸♪
##参照
こちらに答えがありました〜!!ありがとうございます٩(ˊᗜˋ*)و
[Laravel] Eloquent リレーションと Eager Loading
Laravel with(Eagerロード)の使い方・サンプル付き
##反省
そもそも、Eagerロードという名前・書き方にたどり着くまでにかなり時間がかかってしまった。一つ一つ経験を積み上げないとダメだなぁ・・・(´;ㅿ;`)