環境
Laravel: 8.82.0
PHP: 8.1.2
OR検索
文字列で検索ができるようにOR検索を導入しようとしたときでした。
とりあえず少し調べて、以下のコードで実行しました。
$keyword = $request->keyword;
$user = Auth::user();
$items = $user->items()->where("name", "like", "%".$keyword."%")
->orWhere("text", "like", "%".$keyword."%")->get();
これで OR 検索をしようとすると、
->where("name", $keyword)は、ログインしている User の結果だけを返すのですが、
->orWhere("text", $keyword)は、全ての Userの結果を返すのです。
なんでやねん!
とツッコミを入れながらも、改善案を探すことにしました。
ダメな理由
ダメな理由は条件の書き方にありました。
$userのitemに対してnameまたはtextがキーワードを含んでいたら、というつもりで書いていました。
つまり、itemに対しての条件で考えていたのです。
しかし、先程の記述では
$userのitemに対してnameがキーワードを含む
または、$userに対してtextがキーワードを含んでいたらになっていたのです。
その際に、$userにtextというカラムが存在しないため、
全件一致という扱いを受けて、実質$user->all()(全ての User)と同義になってしまったという訳です。
これでは想定している動作とは異なるので、
条件の書き方を修正する必要があります。
whereでまとめる
検索してみると、whereをまとめることができるようです!
早速まとめてみます!
$keyword = $request->keyword;
$user = Auth::user();
$items = $user->items()->where(function ($query) {
$query->where("name", "like", "%".$keyword."%")
->orWhere("text", "like", "%".$keyword."%");
})->get();
これで大丈夫と思ったのですが、エラーが出ます。
スコープの問題で、「$keywordなんて知らない」といわれます。
これでも悩みました。。。
useで変数を渡す
そうしていると、先輩が解決方法を教えてくださり、
$keyword = $request->keyword;
$user = Auth::user();
$items = $user->items()->where(function ($query) use ($keyword) {
$query->where("name", "like", "%".$keyword."%")
->orWhere("text", "like", "%".$keyword."%");
})->get();
useを使って解決できたのでした。