Laravel

【Laravel・whereHas】リレーション先でクエリ実行

こんにちは!ともです(@_tomo_engineer)!

この記事ではLaravelでリレーション先の情報を元にクエリを発行し、絞り込む方法についてのまとめます。

Laravel5.5では確認済みです!

目次

  • 想定するテーブルと条件について
  • リレーション先の情報を元にクエリの作成
  • その他の手段

想定するテーブルについて

hasManyの関係にあるテーブルを想定します。

usersテーブルとpostsテーブルを考えます。

ある投稿者(user)が複数の投稿(post)を行う、Twitterにありそうな投稿テーブルです。

  • users_table
id name created_at
1 田中 2018-09-01
2 鈴木 2018-09-02
3 山田 2018-09-03
  • posts_table
id user_id content created_at
1 1 田中の投稿1 2018-09-02 09:00
2 1 田中の投稿2 2018-09-03 21:00
3 1 田中の投稿3 2018-09-04 19:00
4 2 鈴木の投稿1 2018-09-02 08:00
5 3 山田の投稿1  2018-09-05 19:00

 

users_tableのidとposts_tableのuser_idによって紐ついており、hasManyの関係にあるレコードを同一の色で示しています。

ここで、次のようなクエリを発行したいと考えます。

2018年9月3日0時00分以降に投稿したユーザを取得したい

今回の場合ですと

id user_id content created_at
1 1 田中の投稿1 2018-09-02 09:00
2 1 田中の投稿2 2018-09-03 21:00
3 1 田中の投稿3 2018-09-04 19:00
4 2 鈴木の投稿1 2018-09-02 08:00
5 3 山田の投稿1  2018-09-05 19:00

上記の緑色の投稿が2018年9月3日0時00分以降の投稿ですので、user_id = 1, 3の

ユーザが検索に引っかかる訳です。

ControllerとModel

  • web.php
Route::get('/', 'IndexController@index');
  • IndexController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class IndexController extends Controller
{
    public function index(){
        return view('home');
    }
}
  • User.php(モデル)
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    public function posts(){
        return $this->hasMany('App\Post', 'user_id', 'id');
    }
}

では次に、どのように検索するかについてご説明致します。

リレーション先の情報を元にクエリの生成

リレーション先の情報を利用してクエリを発行したい場合、whereHasが利用できます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;

class IndexController extends Controller
{
    public function index(){

        $user = User::whereHas('posts', function($query){
            $query->where('created_at', '>', '2018-09-03');
        })->get();

        dd($user);

        return view('home');
    }
}

Userモデルで作成したリレーションはpostsでしたので、第一引数にposts

第二引数ではposts_tableを見た際のクエリです。

ですので、この場合のcreated_atはposts.created_atを意味しています。

ddメソッドで確認した結果は以下です。

田中・山田の2名を取得する事ができました。

array[0]が田中さん、arrayが山田さんです。

その他の手段

Laravelの上手く使えばできるんだろうけど、その方法が分からない・・・

そのような場合はクエリビルダでSQLを作成するという手段で乗り切ります

今回の場合ですと、テーブルの結合を行います。

users_tableのidとposts_tableのuser_idでjoinを行う事で検索する事ができます。

  • クエリビルダで
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;

class IndexController extends Controller
{
    public function index(){

        $users = User::
            join('posts', 'users.id', 'posts.user_id')
            ->where('posts.created_at', '>', '2018-09-03')
            ->select('users.*')
            ->distinct()
            ->get();

        return view('home');
    }
}

これにより、users_tableとposts_tableがuser_idを介して接続されたテーブルを作成し後は単純に検索を行うだけです。

ここで注意点はこのテーブルにはcreated_atが2つ存在するという事です。

  • users_tableのcreated_at
  • posts_tableのcreated_at

単純にcreated_atと書くと曖昧(ambiguous)となります。

ですので、

  • users.created_at => usersのcreated_at
  • posts.created_at => postsのcreated_at

とどちらのテーブルのカラムを指すのか明示する必要があります。

まとめ

今回はLaravel5.5でリレーション先の情報を利用してクエリを発行する方法を書きました。

whereHasを利用した方法と、クエリビルダを利用する方法をについて書きました。

下記のLaravel ドキュメントで確認して見ましょう。

  • リレーションについて

https://readouble.com/laravel/5.5/ja/eloquent-relationships.html

  • クエリビルダについて

https://readouble.com/laravel/5.5/ja/queries.html