【Laravel】リレーションの違いを整理してみた

Share on:

リレーションの種類

https://twitter.com/kenji__n/status/1072774024625700864
こちらの記事を参考にしつつ、自分なりに整理するという記事です。
いろいろと省略したいので、先に環境を決めておきます。

// User.php
public function posts()
    {
        return $this->hasMany('App\Post');
    }

あるユーザが複数の Post を持っているというリレーションです。
リレーションの仕組みや実装については、これ以上は省略します。

動的プロパティとは

$user->posts->title

上のようなコードでその User が持っている Post のタイトル一覧を取得することができます。
dd($user->posts)を実行してみると、
Collection という Laravel 特有の配列で出力されています。
つまり、10 件あったとしたら、11 のクエリが発行されます。
これがN+1 問題というやつです。

Eloquent リレーションとは

$user->posts()

この場合、後ろに->title を付けることはできません。
hasMany オブジェクトが返ってきているだけで、Collection ではないからです。
ここが理解に苦しんだ点です。。
1 件 1 件表示するのではなく、User は Post とリレーションしてますよ〜という報告だけが返ってきます。
なので、後ろにはカラム名を付けることはできず、代わりにメソッドを繋げることができます。
カラム名は Post が持っている title とは text とかです。
メソッドは where()とか count()とか get()とか後ろに()が付くやつです。説明が雑ですがこんな感じで整理しました。
メソッドを繋げてみると、

$user->posts()->where('title', 'Hello')->get();

となります。
title カラムの中に Hello が入っているレコードを get しています。
ちなみに get メソッドを使うと、Collection が返ってくるので、ここでようやく title を取得することができます。

EagerLoad とは

先ほどの N+1 問題を解消するためのものです。
動的プロパティを使ってループを書くと、多くの SQL が発行されてしまい、重くなってしまいます。

$users = User::all();
foreach ($users as $user) {
    $user->posts->title;
}

このような書き方をすると、User1 つに対して Post が 10 あったとすると、11 の SQL が発行されます。
それが各 User 分あるので、どんどん大きくなってしまうわけです。
User の数分 SQL が発行されるのは仕方ないのですが、Post 分は 1 つにまとめることができます。
それをしてくれるのが、EagerRoadです。

$users = User::with('posts')->get();
foreach ($users as $user) {
    $user->posts->title;
}

with メソッドを使うと、Post の SQL が 1 つにまとまります。
初心者の内は、クエリの数を見逃すことが多いと思いますが、ページが重いな〜と思ったら SQL の数をチェックしてみてください。

まとめ

動的プロパティと Eloquent リレーションの違いがわからず、テキトウに()をつけたり外したりして、エラーが出ない方を選んでいました。w
これでしっかりと理解できたはずなので、違いがわかる男になったと思います。
簡単にまとめると、後ろにカラムが欲しい場合は動的(カッコなし)で、後ろにメソッドが欲しい場合は Eloquent(カッコあり)で良いはずです。
動的プロパティは SQL が多くなる可能性もあるので、どっちにすれば良いかわからないときは、まずはカッコをつけましょう。
カッコつけた、違いの分かる男になりましょう!