エンジニ屋.com(エンジニヤドットコム)

分かりやすくを意識して情報発信!

【Laravel】リレーション先を基準にデータを並び替える方法【WhereHasは使えない】


本記事は、Laravelでリレーション先を基準に並び替えをする方法を紹介します。

イメージ

この記事を書く背景

学習者へアドバイスやサポートする立場のお仕事に携わっているなかで、 このような質問がありました。


投稿一覧をユーザー名で並び替えを行いたい。
whereHasでリレーション先の並び替えを行っているのに上手く表示されません。
なぜでしょうか?

試していたソースコードは下記となります。

$posts = Post::whereHas('user', function($query){
   $query->orderBy('id', 'asc' );
})->get();

原因は
リレーション先テーブルの中で並び替えを行っているだけなので、抽出されるデータには変わりありません。

ではどうすればリレーション先を基準に並び替えができるのか?

どのようなクエリ文が発行されているか?

whereHasはどいうときに使用されるのか?

シンプルな具体例に沿って確認していきたいと思います

確認のための準備

テーブルとデータは下記のように用意しました。

usersテーブル

postsテーブル

Userモデルに関してはデフォルトのものを使用します。

もしPostテーブルを作成からテストデータ挿入方法を学びたい方はこちらの記事を参考にして下さい!

enginiya.com

テーブルとデータが作成されたらModelファイルにリレーションを定義します。

Models/Post.php

//省略
public function user()
{
 $this->belongsTo(App/Models/User::class);
}
//省略

Postテーブルから見ると多対1の関係となります。

登録された投稿を一覧表示するためのソースコードを用意します。 index.blabe.php

<table style="margin: 30px auto 0;" border=3>
        <tr>
            <th>投稿ID </th>
            <th>ユーザー名</th>
            <th>投稿内容</th>
        </tr>
        @foreach ( $posts as $post )
        <tr>
            <td style="text-align: center">
                {{ $post->id }}
            </td>
            <td>
                {{ $post->user->name }}
            </td>
            <td>
                {{ $post->body }}
            </td>
        </tr>
        @endforeach
    </table>

一覧を表示させる為のシンプルなコードです。

bladeへ渡す変数を下記のように

PostController.php

use App\Models\Post;

class PostController extends Controller
{
    public function index()
    {
        \\ここに記述を書いていきます。

        return view('admin.post.index', compact('posts'));
    }
}

それでは準備が整ったので順番に見ていきます。

まずは普通に一覧表示

$posts = Post:all();

結果

whereHas

$posts = Post::whereHas('user', function($query){
            $query->orderBy('id', 'asc');
        })->get();

結果

結果は変わりありません。

どのようなSQLが発行されているかtoSql()メソッドで確認したいと思います。

$posts = Post::whereHas('user', function($query){
            $query->orderBy('id', 'asc');
        })->toSql();
dd($posts);

結果

select * from `posts` where exists (select * from `users` where `posts`.`user_id` = `users`.`id` order by `id` asc)

whereの中でサブクエリで並び替えを行っているだけで、呼び出しているデータはpostsデータとなるので、 並び替えに影響しません。

例えば、idが3のみのユーザーで絞り込みを行いたい場合はwhereHasの出番です。

$posts = Post::whereHas('user', function($query){
            $query->where('id', 3);
        })->get();

結果

SQLはこちらとなります。

select * from `posts` where exists (select * from `users` where `posts`.`user_id` = `users`.`id` and `id` = 3)

ユーザーid で絞込みをしてから、存在するデータのみを呼び出しています。

リレーションを条件に並び替え

では冒頭の本題です。 最初に記述方法を記載します。

$posts = Post::select('*')
            ->join('users','posts.user_id','=','users.id')
            ->orderBy('users.name','asc')
            ->get();

Eloquentのクエリビルダを使用します。

SQLはこちらです。

select * from `posts` inner join `users` on `posts`.`user_id` = `users`.`id` order by `users`.`name` asc

join()でpostテーブルのidとuser_idを紐づけたことによって、user_idを並び替えるとidが並び変わります。

結果

思った通りの結果となりました。

お疲れ様でした!

「参考になったー✌」という方は下の方にある星マークを押してもらえると喜びます。 またTwitterのフォローしてもらえると幸いです!