LoginSignup
71
72

More than 5 years have passed since last update.

[Laravel 5.7] Eloquent リレーションのクエリまとめ

Last updated at Posted at 2018-10-29

リレーションにクエリを投げる時、「hasだっけ?whereHasだっけ?」となることがちょくちょくあるので、 Eloquent: Relationships > Querying Relations を読んでまとめてみた。

公式ドキュメント

2018.12.5 追記

5.7 のドキュメントで追記された内容についても記載したので、↑のURLも5.7に変更しました。

単純にクエリを投げる

Eloquentの全てのタイプのリレーションはメソド経由で定義され、リレーションにもクエリビルダが使えるのでメソドチェーンでかける。
例えば User モデルに Posts モデルがリレーションがされている場合、

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Get all of the posts for the user.
     */
    public function posts()
    {
        return $this->hasMany('App\Post');
    }
}
$user = App\User::find(1);

$user->posts()->where('active', 1)->get();

こんな感じで posts() からさらに制約が追加できる。
もちろん where メソド以外のクエリビルダメソドも使えます。

リレーションが存在しているか、のクエリ

リレーションの存在有無を元にレコードを取りたいときは has orHas メソドが使える。

// comments がある Post だけをとってくる
$posts = App\Post::has('comments')->get();

// 3つ以上の comments がある Post だけをとってくる
$posts = App\Post::has('comments', '>=', 3)->get();

// ネストされたリレーションも .(ドット) を使って対象にできる
$posts = App\Post::has('comments.votes')->get();

もっとカスタマイズしたい場合は whereHas orWhereHas を使う

$posts = App\Post::whereHas('comments', function ($query) {
    $query->where('content', 'like', 'foo%');
})->get();

リレーションが存在していない、のクエリ

さっきと逆だが使い方はほぼ同じで、 doesntHave orDoesntHave を使えばよい。

// comments がない Post だけをとってくる
$posts = App\Post::doesntHave('comments')->get();

よりカスタマイズしたい場合は whereDoesntHave orWhereDoesntHave

$posts = App\Post::whereDoesntHave('comments', function ($query) {
    $query->where('content', 'like', 'foo%');
})->get();

// ネストされたリレーション
$posts = App\Post::whereDoesntHave('comments.author', function ($query) {
    $query->where('banned', 1);
})->get();

リレーションモデルのレコード数をとる

なんと、「リレーションデータの内容は読み込まず、レコード数だけをとる」という方法がある。(知らなかった)
withCount メソドを使って、カラムを {relation}_count とするだけ。超簡単。

// comments のレコード数をとる
$posts = App\Post::withCount('comments')->get();

foreach ($posts as $post) {
    echo $post->comments_count;
}

// comments、votes のレコード数をとる
$posts = App\Post::withCount(['votes', 'comments' => function ($query) {
    $query->where('content', 'like', 'foo%');
}])->get();

echo $posts[0]->votes_count;
echo $posts[0]->comments_count;

また、レコード数にエイリアスをつけることも可能。

$posts = App\Post::withCount([
    'comments',
    'comments as pending_comments_count' => function ($query) {
        $query->where('approved', false);
    }
])->get();

echo $posts[0]->comments_count;

echo $posts[0]->pending_comments_count;

withCountselect を一緒に使うときは順序に注意

5.7 ドキュメントに追記されている内容。
(新機能というより、注意書きとして追記されたと思われる)

select のあとに withCount を呼び出してね と。

$query = App\Post::select(['title', 'body'])->withCount('comments');

echo $posts[0]->title;
echo $posts[0]->body;
echo $posts[0]->comments_count;

まとめ

  • リレーションはメソドで定義する
    • リレーションメソドにはクエリビルダが使え、チェーンメソドが書ける
  • 「リレーションが存在しているか」の判定には has orHas
    • もっとカスタマイズしたいときは whereHas orWhereHas
  • 「リレーションが存在していないか」の判定には doesntHave orDoesntHave
    • もっとカスタマイズしたいときは whereDoesntHave orWhereDoesntHave
  • リレーションモデルのレコード数をとるには withCount を使って、 {relation}_count で取れる
    • エイリアスをつけることも可能
    • select と一緒に使うときは順序に注意
71
72
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
71
72