【Ruby on Rails】gem 'ransack' を利用した検索機能/フォームの実装

検索機能をgemを使って簡単に実装しましょう！

# gem 'ransack'を利用した検索機能実装


[![Image from Gyazo](https://i.gyazo.com/1bae63eb630edcd5cc813259df9daf79.gif)](https://gyazo.com/1bae63eb630edcd5cc813259df9daf79)

## インストール
```ruby:Gemfile

gem 'ransack'
```

インストールしましょう

```:ターミナル

$ bundle install
```

## 検索機能の作成

```ruby:application_controller.rb

before_action :set_search

def set_search
  @search = Product.ransack(params[:q]) #ransackの検索メソッド
  @search_products = @search.result(distinct: true).order(created_at: "DESC").includes(:user).page(params[:page]).per(5) # productsの検索結果一覧 
   # 最終的に、@search_productsを検索結果画面（例：search.html.haml）に挿入します。
   # 検索結果の一覧：  @search_products = @search.result.order(created_at: "DESC")
   # distinct: trueは検索結果のレコード重複しないようにします。
   # ページネーション:  .includes(:user).page(params[:page]).per(5
end
```

application_controller.rbで定義します。

products_controller.rbに書くとproducts_controller.rbを使わないページを表示した時にエラーになるため、application_controller.rbで記述。
kaminariでページネーションをつけているのでpage(params[:page])をつけた。
searchメソッドよりransackメソッド推奨なのでransackメソッドを使った。

#### ransackの検索オプション
| 検索方法 | 意味(英語) | 意味 |
|:--|:--|:--|
| *_eq | equal | 等しい |
| *_not_eq | not equal | 等しくない |
| *_lt | less than | より小さい |
| *_lteq | less than or equal | より小さい（等しいものも含む） |
| *_gt | grater than | より大きい |
| *_gteq | grater than or equal | より大きい（等しいものも含む） |
| *_cont | contains value | 部分一致（内容を含む） |

今回使ったのは*_contですね

[distinct: trueの参考になるページ](https://www.sejuku.net/blog/69900)

### 検索フォームの作成

```haml:検索.html.haml
.search_wap
   = search_form_for @search do |f|
     = f.text_field :name_cont , placeholder: "何かお探しですか？", class: 'input'
       = button_tag type: 'submit', class: 'search__button' do
         .icon_wap
           %i.fas.fa-search
```
haml形式でフォームを作成した。
この段階では、url:〇〇_pathはつけていません。あとで付与していきます。
scssのmixinも記載しておきます。

```scss:mixin_search
@mixin input{
  border: none;
  border-radius: 50px;
  background: #F4F8F9;
  height: 37px;
  width: 100%;
  padding: 10px 20px;
}

.search_wap{
  margin-right: 15px;
  position: relative; 
  .input{
    @include input;
    margin: 13px 0 0;
  }
  .search__button{
    background-color: transparent;
    cursor: pointer;
    position: absolute;
    right: 0px;
    top: 13px;
    height: 37px;
    width: 40px;
    border-style: none;
    border-radius: 0px 4px 4px 0px;
    padding: 0px;
    .icon_wap{
      i{
        position: absolute;
        right: 14px;
        top: 13px;
        font-size: 13px;
        font-weight: 900;
        color: #999999;
      }
    }
  }
}
```
### 検索結果のview作成

あとは、検索画面を作成して、検索結果である@search_productsを挿入してあげれば完成です。


検索結果用の画面に移行するために、route.rbにget :searchを追加します。

```ruby:route.rb
Rails.application.routes.draw do
  resources :products do
    collection do
      get :search
    end
  end
end
```

では、検索フォームにurlを追加しましょう

```haml:検索.html.haml
.search_wap
   = search_form_for @search, url: search_users_path do |f|
     = f.text_field :name_cont , placeholder: "何かお探しですか？", class: 'input'
       = button_tag type: 'submit', class: 'search__button' do
         .icon_wap
           %i.fas.fa-search
```


view用のhtml.hmlとコントローラーを記述します。

```ruby:products_controller.rb
class UsersController < ApplicationController
  def search
  end
end

```

```haml:search.html.haml
= render 'shared/main-header'
.container-fluid
  .row.py-5.w-100
    .jscroll
      = render partial: 'user', collection: @search_products, as: "product", class: "jscroll"

// @search_productはapplication_controller.rbで定義している。
```

@search_productで検索結果のProductを表示させるので、indexで作成したviewに@productsを@search_productに書き換えれば、出来上がりです。


## まとめ


検索処理は下記だけ

```ruby:application_controller.rb

before_action :set_search
def set_search
  @search = Product.ransack(params[:q]) #ransackの検索処理
  @search_products = @search.result # 検索結果
end
```

検索フォームを作って

```haml:検索.html.haml
.search_wap
   = search_form_for @search do |f|
     = f.text_field :name_cont , placeholder: "何かお探しですか？", class: 'input'
       = button_tag type: 'submit', class: 'search__button' do
         .icon_wap
           %i.fas.fa-search
```

あとはviewに@search_productsを挿入すれば完成。


## if文を追加する

if文を追加するなら、下記の３パターンがいいかもしません

```haml:例

// 検索キーワードなし: 空白だとスペースを入れいてるname全部がヒットしてしまうため
- if params[:q]['name_cont'] == ""
  = "検索キーワードがありません。"

// 検索結果ありの場合
- elsif @search_products.present?
  = "「#{params[:q][:name_cont]}」の検索結果: #{@search_products.count}個"

// 検索数0の場合
- else
  = "検索に一致する商品はありませんでした"
```


では実際に書いていきましょう！

## 複数カラムでの検索方法
[参考になるページ](https://qiita.com/nojinoji/items/e1b174220da8c81a1756#%E8%A4%87%E6%95%B0%E3%82%AB%E3%83%A9%E3%83%A0%E3%81%A7%E3%81%AE%E3%82%AD%E3%83%BC%E3%83%AF%E3%83%BC%E3%83%89%E6%A4%9C%E7%B4%A2)

## 複数キーワードをANDで検索する
[参考になるページ](https://suin.io/549)

## 参考リンク

[ransackでRailsアプリのヘッダーに検索機能をつける](https://qiita.com/fujitora/items/b2134bf6abcfda79c47f)
[【Rails】ヘッダーへの検索機能の付け方](https://qiita.com/fukuda_fu/items/58e3b5d9a776d98a62de)
[Rubyon Rails で検索機能を作ろう（ransack）](https://qiita.com/nojinoji/items/e1b174220da8c81a1756)
[[Rails]ransackを利用した色々な検索フォーム作成方法まとめ](https://qiita.com/nishina555/items/2c1f8bae980e426519bc)
[Rails 5.1とBootstrapで作るシンプルな検索機能のテンプレ](https://morizyun.github.io/blog/search-form-rails-bootstrap/)