Help us understand the problem. What is going on with this article?

Laravel/PHP タグ付き投稿の検索機能(orWhereHasと無名関数、クロージャ)

やりたいこと

ジャズライブの口コミをタグ付きで投稿できるサイトを、チームで開発しています。
口コミ投稿をキーワード検索するコードをネットを参考にしながら書いたのですが、一部読み解けなかった部分を整理したいと思います。

仕様

  • キーワードが入力されると、そのキーワードを含む投稿を検索しビューに表示する。
  • 検索対象は、投稿の(1)タイトル、(2)本文、(3)投稿につけられたタグ。
  • 投稿のテーブル、タグのテーブルはEloquentでModelを利用している。
  • 投稿のテーブルとタグのテーブルが中間テーブルを挟んで多対多のリレーションでつながっている。

実装してみた

コントローラの実装例

ルート、ビューなどは省略させていただきます。もし見せて!という方いたらコメントください(いないと思いますが汗)。

SearchController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Review;
use App\Tag;

class SearchController extends Controller
{
    public function search(Request $request)
    {
        $keyword = $request->input('keyword');
        if (!empty($keyword))
        {
            $reviews = Review::where('title', 'like', '%' . $keyword . '%')
            ->orWhere('text', 'like', '%' . $keyword . '%')
            ->orWhereHas('tags', function ($query) use ($keyword){
                $query->where('tag_name', 'like', '%' . $keyword . '%');
            })
            ->paginate(10);
        } else {
            return redirect('/');
        }

        return view('reviews.search', [
            'reviews' => $reviews
        ]);
    }
}

orWhereHasからの2行がよくわからない!!ココを解明したい!!

orWhereHasとは?

            ->orWhereHas('tags', function ($query) use ($keyword){
                $query->where('tag_name', 'like', '%' . $keyword . '%');

orWhereHasはEloquentのメソッド、関数。わかってたふりしてましたが、案外うまく説明できなかったので整理。公式ドキュメント→Eloquent:存在するリレーションのクエリ
orは、2つ以上の条件をつけて「AもしくはB」としたい時に、2つめ以降の条件のメソッドにつける言葉。
Whereは、SQLのわかる方なら想像しやすいかもですが、問い合わせに条件をつけるもの。例えばidがホニャララなもの、とかcreated_atがいついつ以前のものといった条件が想定できるかと思います。
Hasはあるモデルからリレーション先のテーブルに対してレコードを探してくれるメソッド。リレーション名を第一パラメータに渡します。例えば、以下のようにするとタグの付いたレビューを取得するという命令になります。

$reviews = Review::has('tags')->get();

まとめるとorWhereHasは、リレーション先にレコードを(Has)、条件付きで(Where)、並列の条件(or)として探しにいく時に便利なEloquentのメソッドです。

orWhereHasの第二パラメータの関数は何?

            ->orWhereHas('tags', function ($query) use ($keyword){
                $query->where('tag_name', 'like', '%' . $keyword . '%');

このfunctionから始まる2行は、無名関数、またの名をクロージャというやつみたいです。公式→PHP 無名関数。名前ないですものね。無名関数はどういう時に使うかというと、プログラムの中でその場で一度しか使わないものに適切みたいです。ちなみに、Closureというクラスで実装されているそうです。紐解いていきます。

順番前後しますが、use ($keyword)は、その関数の親のスコープから変数$keyordを引き継ぐ時にuseで渡すようです。
($query)は変数ではなく引数。通常の引数を受け付けています。この無名関数の中では、「tag_nameに$keywordを含んだレコードを」という条件設定をしているwhereメソッドにアクセスしています。

総合して一言でこの第2パラメータを説明すると、親スコープの$keywordを引き継いで作成する、whereという条件設定をする関数という感じでしょうか。

最後に

もしご指摘などあれば、バシバシお願い申し上げます!

kakudaisuke
フランス語を話すなどするプログラミング初心者です。Qiitaは自分が学習したことのアウトプットの場として重宝しています。
https://note.com/kakudaisuke
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away