【Laravel】クエリビルダーでスペース区切りの複数ワード検索を行う
所感とか
所感
🤪 考えるのすごい時間かかったのに、寝たら一瞬で思いついた. 睡眠は大事.
大事だと思ったこと
ポイント
- 半角スペースを全角スペースにする:
mb_convert_kana($request->words, 's');
- スペースごとに配列に格納:
preg_split('/[\s]+/', $request->words);
- Illuminate\Support\Collectionで配列要素全てに処理を追加:
Collection::make($strArry)->map(function($p){return "%".$p."%";})->toArray();
- それぞれごとにwhere句を形成する:
$query->where(function($query)use(...){});
を複数使うとこでまとまったwhere句を形成できる - 最初に受け取ったinputを引き出せる:
$request->session()->getOldInput()
ソース
// 検索ワード[aaa bbb ccc] の場合 select * from `products` where (`title` like "%aaa%" and `title` like "%bbb%" and `title` like "%ccc%") or (`description` like "%aaa%" and `description` like "%bbb%" and `description` like "%ccc%");
- Model(Search.php)
<?php static function search(Request $request) { $products = self::query(); $hasParam = true; $search_words; if(isset($request->words)) { //検索ワードを分割 if(isset($request->words)) { //半角スペースを全角スペースにする $request->words = mb_convert_kana($request->words, 's'); //スペースごとに配列に格納 $strArry = preg_split('/[\s]+/', $request->words); //use Illuminate\Support\Collectionで配列要素全てにワイルドカード(%)を追加 //$pが配列の要素ひとつずつになる. その要素に処理をして,returnで元の要素と入れ替えるイメージ $search_words = Collection::make($strArry)->map(function($p) { return "%" . $p . "%"; })->toArray(); } $count = count($search_words); //title, descriptionそれぞれごとにwhere句を形成 $products = $products ->where(function($query)use($search_words){ foreach($search_words as $search_word) { $query->where('title', 'like', $search_word); } }) ->orwhere(function($query)use($search_words){ foreach($search_words as $search_word) { $query->where('description', 'like', $search_word); } }); } else { $hasParam = false; $products = $products; } $result = []; //初期表示にメッセージが出ないようにする if($products->count() <= 0 && $hasParam){ $result['message'] = "検索結果は0件です"; } else { $result['message'] = ""; } $result['products'] = $products; return $result; }
- Controller(Controller.php)
<?php public function index(Request $request) { $products = Search::search($request); $message = $products['message']; $products = $products['products']; if($message) { //検索結果が0件の場合 return view('index', [ 'products' => $products, 'inputs' => array_merge($request->input(), $request->session()->getOldInput()), 'message' => $message ]); } //検索結果が1件以上の場合 return view('akashic-game.products.index', [ 'products' => $products, 'inputs' => array_merge($request->input(), $request->session()->getOldInput()), 'message' => $message ]); }
- View(index.blade.php)
@if (isset($message)) <p>{{ $message }}</p> @else <form action="{{ action('Controller@index') }}" method="GET"> <fieldset> <div> <input type="text" name="words" value="@if(!empty($inputs['words'])){{ $inputs['words'] }}@endif"> </div> <button type="submit">検索</button> </fieldset> </form> <div> @forelse($items as $item) <li>{{ $item->title }} : {{ $item->description }}</li> @endforeach </div>