ransackの検索機能
今回はgem ransack
で検索機能を実装します。
以前の記事でフリーワード検索を実装したのですが、今回は作成日での検索とセレクトボックスでの検索を実装していきます。
1つ前の記事で作ったscaffoldアプリを元にやっていきます。
このアプリではi18n、enum、enum_helpを導入済みです。
ではいってみましょう。
ransackをインストール
gemfileに
gem 'ransack'
として
bundle install
します。
コントローラー編集
コントローラーで検索結果を受け取れるようにします。
def index # @users = User.all これを削除 @q = User.ransack(params[:q]) #この2行を @users = @q.result(distinct: true) #追加します end
ransackメソッド
送られてきたパラメーターを元にテーブルからデータを検索するメソッド
(params[:q])
検索パラメータを取得
resultメソッド
ransackメソッドで取得したデータをオブジェクトに変換するメソッド
(distinct: true)
検索結果の重複を取り除く
viewの編集
ついでにviewをちょっと見やすく編集します。
users/index.html.erb <p id="notice"><%= notice %></p> <h1>Users</h1> <table> <thead> <tr> <th>Name</th> <th>Gender</th> <th>Created_at</th> <th colspan="3"></th> </tr> </thead> <tbody> <%= render @users %> </tbody> </table> <br> <%= link_to 'New User', new_user_path %>
users/_user.html.erb <tr> <td><%= user.name %></td> <td><%= user.gender_i18n %></td> <td><%= user.created_at %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr>
ユーザー情報部分を部分テンプレートとして切り出して、作成日時も表示されるようにしました。
検索フォームの作成
セレクトボックスで検索
まずはセレクトボックスで、性別で検索できるようにします。
users/index.html.erb <p id="notice"><%= notice %></p> <h1>Users</h1> <%= search_form_for @q do |f| %> <%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]} %> <%= f.submit %> <% end %> <table> <thead> <tr> <th>Name</th> <th>Gender</th> <th>Created_at</th> <th colspan="3"></th> </tr> </thead> <tbody> <%= render @users %> </tbody> </table> <br> <%= link_to 'New User', new_user_path %>
search_form_for
ransackが提供するメソッド
f.selectのところがややこしいので1つ1つ見ていきます。
まず第一引数の:gender_eq
ですが、選択した性別と等しいものを探すという意味です。eqはイコール(equal)の略ですね。
そしてややこしい第二引数。
User.genders_i18n.invertまでは前回の記事を参照してください。
irb(main):001:0> User.genders_i18n.invert => {"男性"=>"male", "女性"=>"female", "秘密"=>"secret"} irb(main):002:0> User.genders_i18n.invert.map{|key,value|[key,User.genders[value]]} => [["男性", 0], ["女性", 1], ["秘密", 2]]
この2つを比べてみます。
違いは
- ハッシュから配列に変わっている
- male, female, secretから0, 1, 2に変わっている
の2点、違いがあります。
ハッシュから配列に変わっているのはmapメソッド
によるものです。
mapメソッドは、配列orハッシュ.map {|変数名| 実行する処理 }
で戻り値を配列で返します。
今回のややこしいところに当てはめてみると、
irb(main):001:0> User.genders_i18n.invert => {"男性"=>"male", "女性"=>"female", "秘密"=>"secret"}
これを配列の形になおして欲しい。
配列の中身は|key, value|という形にしてくれ。
keyはそのままkey("男性"=>"male"の"男性")、
valueはUser.genders[value]つまり
irb(main):003:0> User.genders => {"male"=>0, "female"=>1, "secret"=>2}
のvalueの部分("male"=>0の0)
つまり最終的な形が
irb(main):002:0> User.genders_i18n.invert.map{|key,value|[key,User.genders[value]]} => [["男性", 0], ["女性", 1], ["秘密", 2]]
になる、という流れです。
これでセレクトボックスによる検索が実装できました。
最後に指定なしでも検索できるように
<%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]}, include_blank: '指定なし' %>
include_blank
を設定しておきます。
複数条件で検索する時に必要です。
作成日で検索
次は作成日での検索です。
例えば5/1の00:00から5/10の23:59で検索したいとします。
<%= search_form_for @q do |f| %> <%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]}, include_blank: '指定なし' %> <%= f.date_field :created_at_gteq %> <span>〜</span> <%= f.date_field :created_at_lteq %> <%= f.submit %> <% end %>
date_field
日付の入力欄を生成
gteq
greater than equalの略
lteq
less than equalの略
created_at_gteq
で指定した日付以降
created_at_lteq
で指定した日付まで
こんな感じで日付で検索できます。
しかし実際に検索してみると
5/10の15:00:00に作成したyamadaさんが表示されていません。
created_at_lteqだと、その日の00:00までの範囲で検索してしまうからです。
これを23:59までにするにはpredicate
のカスタマイズが必要です。
predicateのカスタマイズ
まずconfig/initializers/ransack.rbを作成します。
Ransack.configure do |config| config.add_predicate 'lteq_end_of_day', # 名前を付ける arel_predicate: 'lteq', # 'lteq'をカスタマイズしますよ formatter: proc { |v| v.end_of_day } # end_of_dayメソッドを実行 end
これでカスタマイズできたので検索フォームを修正してみます。
<%= search_form_for @q do |f| %> <%= f.select :gender_eq, User.genders_i18n.invert.map{|key, value| [key, User.genders[value]]}, include_blank: '指定なし' %> <%= f.date_field :created_at_gteq %> <span>〜</span> <%= f.date_field :created_at_lteq_end_of_day %> <%= f.submit %> <% end %>
これで
想定通りの検索ができました。
最後に
自分的には難しい内容でした。コンソールを使ったりして流れを追って、なんとかやってることはわかった、ぐらいの感じです。
このロジックを自分で考えるのはちょっと今の理解度では厳しいな、と思いました。アウトプットを続けて知識を定着させていきたいです。
あと参考にしたサイトとかいつも載せてなかったんですが載せないとダメですよね、忘れてました。すいません。
今日は疲れたので次回から載せます…
最後まで読んでいただきありがとうございました。間違いなどありましたらコメントいただけますと幸いです。