【ransack(gem)】Railsアプリで複数のビューで使う検索機能

久しぶりにブログ書くよ

ちょうど二週間前、スクール課題である某フリマサイトのクローンアプリを終わらせて今まで必死に個人アプリを作ってました。

色々と知見も溜まったかな？と思うしこれから就活なのもあるのでまたブログを書いていこうと思ってます。

今日はgem ransackについて

<h2>ransackとは</h2>
rails用の検索機能を実装するためのgemです。
簡単にできます。

実装は非常に簡単で、現場でも選定されやすいgemだとか。

ちなみに下記技術書に記載されてました。現場で使われがちな技術をチョイスしてくれてる本です。
https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Ruby-Rails-5%E9%80%9F%E7%BF%92%E5%AE%9F%E8%B7%B5%E3%82%AC%E3%82%A4%E3%83%89-%E5%A4%A7%E5%A0%B4%E5%AF%A7%E5%AD%90/dp/4839962227

<h2>使い方</h2>

さて本題。

まずいつも通りgemfileに記述する。

```ruby
gem 'ransack' 
```

したらターミナルでbundle

```bash
bundle
```

これからransackをコントローラ側で設定していくことになるんですが、その前に

今回の想定では、ヘッダーに検索フォームを組み混みます。
ヘッダーが使われるページは、トップページ、検索結果ページ、投稿詳細ページに使われます。

つまりアクションとしては3つ。index, seaech, showアクションそれぞれにransackの設定を書くことになるよ。

参考までに見た目はこんな感じ。ちなみに僕が作ったアプリです。

#### indexページ
<img width="1440" alt="68747470733a2f2f692e6779617a6f2e636f6d2f31313538323034316637396639376265333033636633363634376237623730332e6a7067.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434850/d3694303-bb53-4a62-37ee-86e94fcab32e.jpeg">

#### searchページ
<img width="1440" alt="kangaeruhito.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434850/c944cac6-8d52-b773-7d42-6e247166f3ab.jpeg">

#### showページ
<img width="1440" alt="aa.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434850/a4e6ce16-791f-ce40-c137-e67af20061b7.jpeg">



<h2>コントローラ設定</h2>

特にちゃんと設定しないといけないのはsearchアクションになるので、searchアクションから。

いきなり結論ですが、僕の場合はこんな感じになりました。

gears_controller.rb

```ruby
def search
  @search = Gear.search(search_params)
  @gear = @search.result(distinct: true).order(created_at: 'DESC').page(params[:page]).per(10)  
end

def search_params
  params.require(:q).permit(:gearname_or_title_or_review_cont)
end
```

<h3>検索が実行された場合</h3>

<h4>1:まずsearch_params</h4>
paramsから:qというキーの中に入っている値が欲しいので、require(:q)を書きます。

binding.pryとかかけて見てみて。

必要なバリューとしては:gearname_or_title_or_review_contなのでpermit。

やたら長いですが、ビューの方で解説します。

<h4>2:Gear.search(search_params)</h4>

さっき取得したsearch_pramsを使ってGearテーブル内で検索をかけます。

ヒットした値を@searchというオブジェクト化。

<h4>3:@search.result(distinct: true).order(created_at: 'DESC').page(params[:page]).per(10) </h4>

@searchでオブジェクト化したものを@gearというインスタンス変数に変えています。

この@gearという値をビューに渡します。

<h2>ビュー</h2>

対応するビューはこんな感じ。

ヘッダーを部分テンプレートとして分けて、全てのアクションで下記hamlを読み出してます。

要は共通ってこと

_header.html.haml

```haml
.main-header-wrapper
  = search_form_for @search, url: gears_search_path, class: 'container-fluid shadow' do |f|
    .main-header.row.mx-2.py-3.align-self-center
      =link_to root_path, class: 'main-header__logo h4 col-lg-2 col-md-3 col-sm-4 col-6 text-left align-self-center mb-0' do
        MyNewGear
   
    .main-header__search.col-lg-7.align-self-center.mt-2.mt-lg-0.ml-lg-3
      =  f.search_field :gearname_or_title_or_review_cont, class: 'form-control', placeholder: 'キーワードを入力' 
 
```

ransackではsearch_form_forというヘルパーを使います。ここでは@searchをモデルとして指定します。

それと=f.search_fieldを記述します。これでいつも通りのフォームができるよ。

最後に:gearname_or_title_or_review_contという記述。

これは、カラムと検索条件を指定しています。

Gearテーブルのgearnaemカラム or titleカラム or reviewカラムでテキストを含むもの(cont)を検索対象とする。と言う意味です。

contってなんやねん！って感じですね。でもこの記述も重要なんです。許してあげてください。

<h2>indexとshowでも有効にする</h2>

ここまで来たら検索機能の設定はほぼおしまいです。

ただ、indexとshowアクションでの設定が必要です。コントローラに以下のように追記してあげてください。

gears_controller.rb

```ruby

before_action :set_search, only: [:index, :show]

    def set_search
      @search = Gear.ransack(params[:q])
    end

```

公式ドキュメントにも載っていますが、この記述がないとparamsがransackに対応してくれないようです。

最後に記載してしまいましたが、基本的に検索はトップページからすることが多いと思うので、indexアクションに最初から設定しとくのも有りかと思います。