やりたいこと
タグを付けて口コミを投稿することができるサイトを作っています。その投稿のタイトルや本文、タグを対象に検索する機能を実装します。検索自体は難しくないかもしれませんが、複数テーブルにまたがるデータを全て検索する必要があるので、ほんの少し厄介でした。でも想像していたより簡単にできました。
前提
データベースは下記の通り、多対多、お互いbelongsToManyのリレーションになっています。
reviews:投稿
tags:タグ
review_tag:中間テーブル
ちなみに
uses:reviewsテーブルと一対多の関係になっている。
環境:Laravel 7
実装!
ビュー1(検索画面)
今回のアプリはヘッダーのコンポーネントに検索を作りました。iタグはFontawesomeの検索虫眼鏡です。
<form action="{{ url('/search')}}" method="get">
<i class="fas fa-search"></i>
<input id="header-search-text" type="search" name="keyword" placeholder="キーワードで検索">
</form>
ボタンは付けませんでしたが、ユーザーがEnterを叩くと送信されます。
URLはmethod属性をget
にすることで、?
をセパレータとして、action属性のURLに続けて生成してくれます。参考→https://developer.mozilla.org/ja/docs/Web/HTML/Element/form
ビュー2(検索結果表示)
かなり簡素化してますがコーディング例を下に書いておきます。
親レイアウトとして、views/layouts/myapp.blade.phpを用意しています。
head内、titleタグに@yield('title')
というディレクティブを仕込んでいます。
Bladeレイアウトはこちらで→**Laravel7継承レイアウトの基本を図解してみた**
@extends('layouts.myapp')
@section('title')
「{{$keyword}}」の検索結果
@endsection
@section('main')
<h1 class="main-title">「{{$keyword}}」の検索結果</h1>
@foreach ($reviews as $review)
<h2>{{$review->live_date}} {{$review->title}}</h2>
@foreach ($review->tags as $tag)
<a>{{$tag->tag_name}}</a>
@endforeach
<p>{{$review->text}}</p>
<span>by {{$review->user->account_name}}さん</span>
<span>{{$review->created_at}}</span>
@endforeach
{{ $reviews->links('vendor.pagination.bootstrap-4')}}
@endsection
ルート
Route::get('search', 'SearchController@search')->name('search');
コントローラ
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use App\Review;
use App\Tag;
use App\User;
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', [
'keyword' => $keyword,
'reviews' => $reviews
]);
}
}
個人的にポイントは下記です。
- まずwhereメソッドでreviewテーブルのtitleフィールドで検索かける
- orWhereメソッドでtextにも検索かける
- orWhereHasでreviewテーブルにとって多対多の関係にあるtagsテーブルにアクセスし検索をかける
- where、orWhere、orWhereHasの第2パラメータにlike検索の条件設定をする
- %記号で前後に検索キーワードの前後に何があってもOKなので、その検索キーワードが引っかかるようになります
- %を''で囲うのはwhereがパラメータ結合に対応していないため
おしまい
もしご指摘やご質問、うまくいかなかったなどしたら、お気軽にコメントください!