【Laravel】Eloquentのネストされたリレーションを条件付きで取得する方法

【Laravel】Eloquentのネストされたリレーションを条件付きで取得する方法 Laravel

LaravelのEloquentは、リレーションを簡潔に扱える強力なORM機能を提供していますが、ネストされたリレーション(リレーションの中のリレーション)に対して条件を付けて取得したいというケースでは、書き方に少し工夫が必要です。

この記事では、ネストされたリレーションに対して条件を付ける方法とその使い分け方について、具体的なコード例を交えて解説します。

基本構造:リレーションのネスト

例えば、以下のようなリレーション構造を持つモデルを考えます。

  • User は複数の Post を持つ
  • Post は複数の Comment を持つ

このとき、ユーザーと一緒に投稿と、その投稿に対するコメントも取得したい場合は、以下のようにネストしてリレーションを取得できます。

User::with('posts.comments')->get();

ネスト先のリレーションに条件をつけるには

コメントのうち、「承認済み」のものだけを取得したい場合、単純に with() でネストするだけでは実現できません。そこで、クロージャを使った条件付きリレーションの記述が必要になります。

User::with(['posts' => function ($postQuery) {
  $postQuery->with(['comments' => function ($commentQuery) {
    $commentQuery->where('approved', true);
  }]);
}])->get();

このように、with()の中にクロージャをネストして記述することで、深い階層にあるリレーションにも柔軟に条件を付けることが可能です。

has() / whereHas() とネストの組み合わせ

「特定の条件を満たすリレーションが存在するユーザーのみを取得したい」というケースでは、whereHas() を併用することで絞り込みが可能です。

例:承認済みコメントを持つ投稿が存在するユーザーを取得

User::whereHas('posts.comments', function ($query) {
  $query->where('approved', true);
})->get();

posts.commentsというネストされたリレーション名もそのまま指定できます。内部的にはJOINを構築して、条件に合致するレコードのみを返します。

条件付きリレーションとページネーションの注意点

ネストされたリレーションに条件を付けている場合でも、親リレーション(この場合はposts)には制限がかからないため、例えば以下のような処理では注意が必要です。

User::with(['posts' => function ($postQuery) {
  $postQuery->with(['comments' => function ($commentQuery) {
    $commentQuery->where('approved', true);
  }]);
}])->paginate(10);

この場合、ページネーションはUserモデルに対して行われるため、リレーション側の件数には影響しません。必要であればwithCount()hasManyThroughなどを併用することも検討しましょう。

まとめ

Laravelでネストされたリレーションを条件付きで取得するには、以下のポイントを押さえる必要があります。

  • with()の中にクロージャをネストして条件を記述
  • whereHas()で親モデルの絞り込みが可能
  • ページネーションとの兼ね合いにも注意が必要

適切なリレーション設計とクエリ構築により、複雑なデータ構造も効率よく扱うことができます。Eloquentの表現力を活かして、可読性とパフォーマンスに優れたコードを目指しましょう。