はじめに
料理レシピ検索投稿サイト作成に伴い、検索機能の実装しました。
複数の単語で、かつ、関連モデルのカラムに対しても検索できるフォームにしました。
環境
Rails 5.2.4.4
MySQL 5.7.31
テーブル構成
RecipesとIngredientsを多対多の関係で紐づけています。
このrecipesのtitleカラムとingredientsのnameカラムに対して検索できるものを作ります。
(必要のないカラムは省略しています)
実装
1. ルーティング
resources :recipes do
collection do
get 'search'
end
end
2. コントローラー
def search
redirect_to recipes_path if params[:search] == ""
@array_searches = params[:search].split(/[[:blank:]]+/)
@recipes = []
@array_searches.each do |search|
next if search == ""
recipes = Recipe.joins(:ingredients)
@recipes += recipes.where('ingredients.name LIKE ? ', "#{search}%").or(recipes.where('title LIKE ?', "#{search}%"))
end
@recipes.uniq!
end
・redirect_to recipes_path if params[:search] == ""
paramsが空だった場合、recipes_pathに移動させます。
・@array_searches = params[:search].split(/[[:blank:]]+/)
paramsをsplitで分割させますが、split(" ")
だと半角スペースしか対応できません。
空白文字の正規表現[:blank:]
を使用し、連続した空白にも対応できるようにします。
・@recipes = []
空の配列を用意します。
・@array_searches.each do |search|
分割したparamsをひとつずつ検索にかけるため、繰り返し文に入れます。
・next if search == ""
これがないと、先頭に空白が入った時、全てのレコードにヒットします。
・recipes = Recipe.joins(:ingredients)
.joins
を用い、RecipesとIngredientsを内部結合させます。
Ingredientsのnameカラムに対しても検索をかけるためです。
あとは、前方一致で検索をかけ、.uniq!
で重複した検索結果がでないようにします。
3. ビュー
<%= form_with(url: search_recipes_path, local: true, method: :get) do |f| %>
<%= f.text_field :search, placeholder: "Search Recipes" %>
<%= f.submit "Search" %>
<% end %>
参考にした記事
・https://qiita.com/Orangina1050/items/ca4e5dc26e0a32ee3137
・https://qiita.com/nao58/items/bf5d017a06fc33da9e3b