LaravelのEloquent ORMは、テーブル間のリレーションを管理・操作する際の独自の表現があります。
Laravel5.8 Eloquent:リレーション
"1対多"や"多対多"のリレーションなどはよく使うのではないでしょうか。
今回は、これらのリレーションを定義したのち、実際にリレーション先のテーブルのデータをどうやって取得するのか、いくつか手法を調べました。
※ここから先は、予めモデルクラスに以下のリレーションを定義したという前提で進めます。
ブログサイトなどで、1人のユーザーが複数の投稿を登録しているという想定です。
public function posts()
{
return $this->hasMany('App\Posts');
}
1.メソッドで呼び出す
マニュアルに記載されている、基本のやり方です。
// ユーザーに紐づく投稿を全て取得し、Collectionで返す
public function getPosts()
{
return $this
->find(1)
->posts()
->get();
}
// 条件を付け加えることももちろん可能
// ユーザーに紐づく投稿について、題名で部分一致検索するものをCollectionで返す
public function getPostByTitle()
{
return $this
->find(1)
->posts()
->where('title', 'LIKE', '%hoge%')
->get();
}
posts
メソッドが呼び出されると、システムは Posts
モデルクラスを参照します。
ですから、通常のモデルクラスの操作と同じように、メソッドチェーンであらゆる条件などを追加できます。
公式マニュアル曰く
Eloquentは「動的プロパティ」を提供しているので、モデルのプロパティとして定義したリレーションメソッドへアクセスできる
ということで、以下の書き方も可能です
public function getPosts()
{
return $this
->find(1)
->posts; //プロパティで呼び出す
}
さて、筆者も初めはこの方法で全てなんとかなると思っていましたが…ちょっとした落とし穴がありました。
というのも、上記のメソッドで取得できるのは特定のユーザーに紐づく、投稿のデータのみであるため、例えばユーザーのデータと投稿のデータを同時に返したいという場合や、ある条件を満たすユーザーに紐づく投稿を取得するといった場合に不都合が生じたのです。
public function findPostByUserIsOver25yo()
{
return $this
->where('age', '>', '25')
->get()
->posts;
//get()した時点でcollectionオブジェクトになっており、collectionからpostsプロパティを呼び出そうとしているため、エラーになる
}
そのような時はどうすればよいのでしょうか?
2.withメソッドを用いる
このような場合は、 with()
メソッドを使うとうまくいくようです。
参考にさせていただきました
(公式マニュアルよく読んだらひっそりと使われていた…調べて存在を知るまで気がつかなかった)
public function findPostByUserIsOver25yo()
{
return $this
->with('posts')
->where('age', '>', '25')
->get();
}
このように取得すると、 各 User
モデルクラスの relations
プロパティに Post
モデルクラスが紐づいた状態になり、条件に合うユーザーのユーザー情報と投稿データを一度に取得できます。
まとめ
両者を比較した上で、筆者の意見としては全部withでよくね?というのが正直なところです。
特に親テーブルのデータが不要で小テーブルの情報だけ取得したいという場合は、メソッドで呼び出すのがいいのだと思いますが、結局 relations
プロパティで呼び出せるしなぁ…
もう少し色々な挙動を試してみたいです。
ご意見・ご指摘などありましたらコメントお寄せください。