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の表現力を活かして、可読性とパフォーマンスに優れたコードを目指しましょう。