本記事では、シンプルなコードで様々な検索フォームを実装できるgem「ransack」を用いた検索機能の実装手順を詳しく解説します。gemを使用しない、LIKE句を利用したあいまい検索機能の実装方法は、こちらをご覧ください。
Contents
完成画面
・あいまい検索
・並び替え検索
・範囲検索
ransackを用いた検索機能実装の流れ
ransackを用いた検索機能は、大まかに以下の流れで実装します。
①事前準備(ransackのインストール)
②検索フォーム作成
③コントローラ作成[searches_controller.rb]
④ルーティング設定
⑤検索結果画面の作成
実装手順
⓪はじめに
コーディングはhaml/Scssを利用しています。
商品情報一覧が格納されている「itemsテーブル」から、様々な検索方法を通して任意の商品情報を検索、表示させる実装を行なっています。
また検索機能におけるMVCの流れ概念図は、以下記事をご参照ください。
①事前準備(ransackのインストール)
今回の検索機能の実装では、様々な検索機能を短いコードで簡単に作成できる「ransack」というgemを使用します。ransackを使用するには、以下を実行しましょう。
gem 'ransack'
$ bundle install
②検索フォーム作成
まず、以下要素を備えた検索フォームを作成します。
□ 並び替え検索
- 価格の安い/高い順
- 出品の古い/新しい順
□ あいまい検索
- キーワードを追加する
- ブランド名から探す
□ 範囲検索
- 価格
□ 商品の状態
- 配送料の負担
- 販売状況
完成イメージは以下になります。(開閉ボタンクリックで画像が表示されます)
コードは以下になります。(開閉ボタンクリックでコードが表示されます)
.searches
= search_form_for @search, url: items_searches_path do |f|
-# 並び替え検索
.searches-head
%i.fas.fa-angle-down.icon
= f.select( :sorts, { '並び替え': 'id desc', '価格の安い順': 'price asc', '価格の高い順': 'price desc', '出品の古い順': 'updated_at asc', '出品の新しい順': 'updated_at desc' } , { selected: params[:q][:sorts] } , { onchange: 'this.form.submit()'})
.searches-body
.search-body__subtitle
詳細検索
-# あいまい検索(キーワード)
.form-group
%label
%i.fas.fa-plus.icon
%span
キーワードを追加する
= f.search_field :name_cont, class: "input-default", placeholder: '例)値下げ'
-# あいまい検索(ブランド名)
.form-group
%label
%i.fas.fa-tag.icon
%span
ブランド名から探す
= f.search_field :brand_brand_cont, class: "input-default", placeholder: '例)シャネル'
-# 範囲検索(価格)
.form-group
%label
%i.fas.fa-coins.icon
%span
価格
.form_money
= f.number_field :price_gteq, placeholder: "¥ Min"
%span.form_money__fromTo ~
= f.number_field :price_lteq, placeholder: "¥ Max"
-# 絞り込み検索(商品の状態)
.form-group
%label
%i.fas.fa-star.icon
%span 商品の状態
.checkbox
.checkbox-default
%label
%input{type: "checkbox"}
= 'すべて'
%i.check-icon
.checkbox-default
%label
= f.check_box :item_condition_id_in , {multiple: true} , "1", nil
= "新品、未使用"
%i.check-icon
.checkbox-default
%label
= f.check_box :item_condition_id_in , {multiple: true} , "2", nil
= "未使用に近い"
%i.check-icon
.checkbox-default
%label
= f.check_box :item_condition_id_in , {multiple: true} , "3", nil
= "目立った傷や汚れなし"
%i.check-icon
.checkbox-default
%label
= f.check_box :item_condition_id_in , {multiple: true} , "4", nil
= "やや傷や汚れあり"
%i.check-icon
.checkbox-default
%label
= f.check_box :item_condition_id_in , {multiple: true} , "5", nil
= "傷や汚れあり"
%i.check-icon
.checkbox-default
%label
= f.check_box :item_condition_id_in , {multiple: true} , "6", nil
= "全体的に状態が悪い"
%i.check-icon
-# 絞り込み検索(配送料)
.form-group
%label
%i.fas.fa-truck-moving.icon
%span 配送料の負担
.checkbox
.checkbox-default
%label
%input{type: "checkbox"}
= 'すべて'
%i.check-icon
.checkbox-default
%label
= f.check_box :postage_payer_id_in , {multiple: true} , "1", nil
= "着払い(購入者負担)"
%i.check-icon
.checkbox-default
%label
= f.check_box :postage_payer_id_in , {multiple: true} , "2", nil
= "送料込み(出品者負担)"
%i.check-icon
-# 絞り込み検索(販売状況)
.form-group
%label
%i.fas.fa-shopping-cart.icon
%span 販売状況
.checkbox
.checkbox-default
%label
%input{type: "checkbox"}
= 'すべて'
%i.check-icon
.checkbox-default
%label
= f.check_box :trading_status_in , {multiple: true} , "0", nil
= "販売中"
%i.check-icon
.checkbox-default
%label
= f.check_box :trading_status_in , {multiple: true} , "1", nil
= "売り切れ"
%i.check-icon
.search-done-btn
= f.button "クリア", type: "reset", class: "btn-default btn-gray"
= f.submit "完了", class: "btn-default btn-red"
検索フォームに入力された文字列を、items/searches#index に送信しています。
また完成イメージのとおり「並び替え検索」「キーワード、ブランド名によるあいまい検索」「価格による範囲検索」「商品の状態、配送料、販売状況による絞り込み検索」で構成しています。(それぞれの実装箇所はコメントアウトで示しています)
※「_searches_sidemenu.html.haml」は、「/items/searches/index.html.haml」から呼び出している、フォーム専用の部分テンプレートファイルです。
※このhtmlは一例ですので、適宜調整ください。
③コントローラ作成[searches_controller.rb]
searchesコントローラを作成します。
$ rails g controller items::searches
次に、コントローラに以下を記述します。
①の検索フォームに入力された文字列を params[:q] で受け取り、インスタンス変数 @search に代入します。それを @search.result としたものが、検索結果の値となります。値が取得できない場合は、binding.pryでデバックして原因を確認しましょう。
class Items::SearchesController < ApplicationController
def search_params
if params[:q].present?
@search = Item.ransack(params[:q])
@search_result = @search.result
@search_word = @search.name_cont
else
params[:q] = { sorts: 'id desc' }
@search = Item.ransack()
@search_result = Item.all
end
end
end
④ルーティング設定
②のsearchesコントローラのindexアクションで検索した値を表示します。
namespace :items do
resources :searches, only: :index
end
⑤検索結果画面の作成
検索結果画面です。
またこのページは、以下の階層構造で構成しています。
searches/index.html.haml にて、ヘッダー(*1)、検索結果(*2)、サイドメニュー(*3)を部分テンプレートで呼び出しています。
items
_header.html.haml (*1)
searches
index.html.haml
_searches_main.html.haml (*2)
_searches_sidemenu.html.haml (*3)
※このhtmlは一例ですので、適宜調整ください。
= render 'items/header'
.searches_contents
= render 'searches_sidemenu'
= render 'searches_main'
= render 'items/camerabutton
.searches_main
.searches_title
- if @search_word.present?
= @search_word
%span の検索結果
- else
= "検索結果"
- if @search_result.present?
.searches_count
= "1-#{@search_result.count}件表示"
.searches_items
- @search_result.each do |item|
= link_to item_path(item) do
%figure.searches_items__img
- if item.trading_status == 1
.itemSold
SOLD
- item.item_imgs.each do |item_img|
= image_tag item_img.url.url
.searches_item
.searches_item__name
= item.name
.searches_item__details
%ul
%li
= "#{item.price.to_s(:delimited, delimiter: ',')}円"
おわりに
ransackを用いた検索機能の実装手順は以上になります。
実装完了後は、以下手順で単体テストも実施しましょう。
ransackはシンプルな高度で、様々な検索フォームを実装できるため、とても便利なgemですね。またgemを使用しない、LIKE句を使用した検索方法も以下でご紹介していますので、よろしければご参照ください。