はじめに
空白区切りで複数の検索をかけれるようにしたい。
【例】 "Ruby PHP JavaScript" → Ruby
PHP
JavaScript
条件
mac OS "11.2.3 Big Sur"
Laravel Framework "6.20.27"
PHP "8.0.7"
最終的なコード
Controller
とModel
に分かれています。
Controllerで使用している関数をModelの方で定義しています。
Controllers/SearchController.php
class SearchController extends Controller
{
public function __construct(Search $search)
{
$this->search = $search;
}
public function index(Request $request)
{
$query = Recipe::query();
// エスケープ処理
$keyword = $this->escape($request->input('keyword'));
// 空白区切りで文字列を分割
$keywords = $this->search->pregSplit($keyword);
// 検索処理
$recipes = $this->search->searchQuery($query, $keywords);
return view('search.index', [
'recipes' => $recipes,
'keywords' => $keywords,
'searchCount' => $this->search->searchCount($recipes),
]);
}
/**
* 検索欄に文字が入力された際、str_replaceを実行する
* @param string $value
* @return string $value
*/
private function escape(string $value = null)
{
if(!$value) {
return $value;
}
return str_replace(
['\\', '%', '_'],
['\\\\', '\\%', '\\_'],
$value
);
}
}
Models/Search.php
class Search extends Model
{
/**
* 空白区切りで検索欄に入力された場合
* @param string $keyword
* @return Array
*/
public function pregSplit($keyword)
{
// 全角空白→半角空白
$keyword = mb_convert_kana( $keyword, "s" );
return preg_split('/[\p{Z}\p{Cc}]++/u', $keyword, -1, PREG_SPLIT_NO_EMPTY);
}
/**
* 検索処理
* @param \Illuminate\Database\Eloquent\Builder $query
* @var string[]|false $keywords
* @return \Illuminate\Database\Eloquent\Builder $query
*/
public function searchQuery($query, $keywords = null)
{
if(!empty($keywords)) {
foreach ($keywords as $keyword) {
$query->where('title', 'like', "%$keyword%")
->orWhere('cook_time', 'like', "%$keyword%")
->orWhere('ingredients', 'like', "%$keyword%")
->orWhereHas('tags', function($query) use ($keyword) {
$query->where('name', 'like', "%$keyword%" );
})
->orWhereHas('mealType', function($query) use ($keyword) {
$query->where('name', 'like', "%$keyword%" );
})
->orWhereHas('mealClass', function($query) use ($keyword) {
$query->where('name', 'like', "%$keyword%" );
});
}
}
return $query->get()->sortByDesc('created_at')
->load('user', 'stocks', 'tags', 'mealType', 'mealClass');
}
/**
* 検索ヒット数カウント
* @param mixed $recipes
* @return int
*/
public function searchCount($recipes)
{
return $recipes->count();
}
}
各コードの流れ
Controllers/SearchController.php
class SearchController extends Controller
{
public function __construct(Search $search)
{
$this->search = $search;
}
public function index(Request $request)
{
$query = Recipe::query();
// ① エスケープ処理
$keyword = $this->escape($request->input('keyword'));
// ② 空白区切りで文字列を分割
$keywords = $this->search->pregSplit($keyword);
// ③ 検索処理
$recipes = $this->search->searchQuery($query, $keywords);
return view('search.index', [
'recipes' => $recipes,
'keywords' => $keywords,
'searchCount' => $this->search->searchCount($recipes),
]);
}
/**
* 検索欄に文字が入力された際、str_replaceを実行する
* @param string $value
* @return string $value
*/
private function escape(string $value = null)
{
if(!$value) {
return $value;
}
return str_replace(
['\\', '%', '_'],
['\\\\', '\\%', '\\_'],
$value
);
}
}
① エスケープ処理
① のescape()
は下の方に定義してあるように、文字の中に怪しい記号が入っていないかを調べています。
もし指定している記号\\, %, _
があれば\\\\, \\%, \\_
と置き換えてくれています。
② 空白区切りで文字列を分割
$keywords = $this->search->pregSplit($keyword);
// pregSplit()の内容
/**
* 空白区切りで検索欄に入力された場合
* @param string $keyword
* @return Array
*/
public function pregSplit($keyword)
{
// 全角空白→半角空白
$keyword = mb_convert_kana( $keyword, "s" );
return preg_split('/[\p{Z}\p{Cc}]++/u', $keyword, -1, PREG_SPLIT_NO_EMPTY);
}
まず、空白区切りが全角
であれば半角
にしてあげる。
$keyword = mb_convert_kana( $keyword, "s" );
全て半角区切りにした次に、空白ごとに文字を分割してその値を返す。
preg_split('/[\p{Z}\p{Cc}]++/u', $keyword, -1, PREG_SPLIT_NO_EMPTY);
③ 検索処理
空白処理を行い、単語のみとなったため最後に検索をかけてヒットしたものを表示。
$recipes = $this->search->searchQuery($query, $keywords);
// 検索処理
public function searchQuery($query, $keywords = null)
{
if(!empty($keywords)) {
foreach ($keywords as $keyword) {
$query->where('title', 'like', "%$keyword%")
->orWhere('cook_time', 'like', "%$keyword%")
->orWhere('ingredients', 'like', "%$keyword%")
->orWhereHas('tags', function($query) use ($keyword) {
$query->where('name', 'like', "%$keyword%" );
})
->orWhereHas('mealType', function($query) use ($keyword) {
$query->where('name', 'like', "%$keyword%" );
})
->orWhereHas('mealClass', function($query) use ($keyword) {
$query->where('name', 'like', "%$keyword%" );
});
}
}
return $query->get()->sortByDesc('created_at')
->load('user', 'stocks', 'tags', 'mealType', 'mealClass');
}
おわり
正規表現あたりが今だに慣れないので、少しずつ慣れていきたい。