ransackを利用して検索機能を実装します。
今回も初心者向けにレシピ投稿アプリを例に作成していきます。
また、検索機能の実装にActive Hashの値も利用するのでActive Hashがわからない方は前回記事にしておりますので、そちらをご覧頂けたらと思います。
完成イメージ
それでは、実装していきましょう!
ransackとは
ransackはRails用の検索機能を実装するためのGem。公式ドキュメント
導入方法
Gemfileに下記を記述しbundle installします。
gem 'ransack'
bundle install
デモデータを投入しよう!
seed.rbにデモデータ生成する記述をしていきます。
category_idとtime_required_idはActive Hashの値と紐付いているカラムになります。
また、time_required_idのFakerがわからない方は前回記事にしておりますので、そちらをご覧頂けたらと思います。
【超かんたん】Fakerを使ってダミーデータを作成しよう!
5.times do |n|
Recipe.create!(
title: "すし・魚料理#{n}",
text: "作り方",
category_id: 1,
time_required_id: Faker::Number.within(range: 2..6)
)
end
# 中略
5.times do |n|
Recipe.create!(
title: "お菓子・スイーツ#{n}",
text: "作り方",
category_id: 10,
time_required_id: Faker::Number.within(range: 2..6)
)
end
rails db:seed
検索機能の実装
ルーティングの設定
まずは検索ページ(search)のルーティングと検索結果を表示するページ(result)のルーティングの設定をしましょう。
7つのアクション以外のルーティングを設定するので、recipesにネストさせます。
今回は、URLにidがつかないのでcollectionを利用します。
Rails.application.routes.draw do
root to: 'home#index'
devise_for :users
resources :users
resources :recipes do
collection do
get :search
get :result
end
end
end
コントローラーの編集
以下の記述は公式ドキュメントを参考にしております。
class RecipesController < ApplicationController
before_action :search_recipes, only: [:search, :result]
def index
@recipes = Recipe.all
end
#中略
def search
end
def result
@results = @q.result
end
private
def search_recipes
@q = Recipe.ransack(params[:q])
end
end
params[:q]のキー「:q」でrecipesテーブルからレシピ情報を探し「@q」に格納します。
この@qに対して「.result」とすることで検索結果を取得します。
次にビューファイルを作成していきましょう。
ビューの作成
ビューファイルの作成
touch app/views/recipes/{search.html.erb,result.html.erb}
ビューファイルの編集
今回はCSSの説明は省きます。
検索フォームにはsearch_form_forというメソッドを使います。
form_withのransack版というイメージです。
form_withでは「text_field」ですが、search_form_forだと「search_field」になります。
また、「:カラム名_マッチャ」とすることで条件にあった検索を行います。
「_cont」だと「入力された値が含まれている」という意味になり、「_eq」は「入力された値と等しい」という意味のマッチャになります。
その他のマッチャについては公式ドキュメントを参照してください。
collection_selectメソッドについては前回の記事とRailsドキュメントを参照してください。
以下のように編集していきます。
<div class="recipe-form">
<h1 class="text-center">検索する</h1>
<%= search_form_for @q, url: result_recipes_path do |f| %>
<div class="form-group">
<label class="text-secondary">料理名</label><br />
<%= f.search_field :title_cont, class: "form-control"%>
</div>
<div class="form-group">
<label class="text-secondary">カテゴリー</label><br />
<%= f.collection_select(:category_id_eq, Category.where.not(id: 0), :id, :name, include_blank: '指定なし') %>
</div>
<div class="form-group">
<label class="text-secondary">所要時間</label><br />
<%= f.collection_select(:time_required_id_eq, TimeRequired.where.not(id: 0), :id, :name, include_blank: '指定なし') %>
</div>
<div class="form-group">
<label class="text-secondary">フリーワード</label><br />
<%= f.search_field :text_cont, class: "form-control"%>
</div>
<div class="actions">
<%= f.submit '検索', class: "btn btn-primary" %>
</div>
<% end %>
</div>
次に検索結果ページを作成していきましょう。
以下のように編集していきます。
<div class="recipes-index text-center">
<h1 class="result-index">検索結果</h1>
<% if @results.length != 0 %>
<% @results.each do |recipe| %>
<div class="recipe">
<div class="recipe-title">
<%= recipe.title %>
</div>
<div class="recipe-content">
カテゴリー: <span class="recipe-category"><%= recipe.category.name %></span>
所要時間: <span class="recipe-time"><%= recipe.time_required.name %></span>
</div>
</div>
<% end %>
<% else %>
該当するレシピはありません
<% end %>
</div>
if文で該当する検索結果がない場合は「該当するレシピはありません」と表示させるようにしています。
それでは実際に検索してみましょう。
該当する検索結果がある場合
該当する検索結果がない場合
以上で完成です。