完成イメージ図
前提
ransackとenum/enum_helpの導入や設定をしていること。
【実装】日付選択検索
「◯◯月◯◯日〜◯◯月◯◯日」のようにするため、predicate(述語)を使っていく。
predicate(述語)とはransackを使った検索フォームで使用できる検索述語のこと。cont(部分一致)や_eq(イコール)に該当する。
「◯◯月◯◯日〜◯◯月◯◯日」を文章で表すと、
「◯◯月◯◯日 以上 ◯◯月◯◯日 以下 」、つまり記号で表すと
「◯◯月◯◯日 ≤ ≥◯◯月◯◯日 」になる。
≤ はより大きいか等しい、述語だと**_gteqに該当する。
≥ はより小さいか等しい、述語だと_lteq**に該当する。
**_gteqと_lteq**を使ってコードに表記してみる。
<%= search_form_for @search, url: admin_users_path do |f| %>
<%= f.date_field :created_at_gteq%>
<span>~</span>
<%= f.date_field :created_at_lteq%>
<%end%>
しかし上記のコードで、1月1日〜1月7日まで検索をかけても、1月7日分は表示されない。
SQLを見てみると
SELECT COUNT(DISTINCT "boards"."id") FROM "boards" WHERE ("boards"."created_at" >= '2022-01-01 00:00:00' AND "boards"."created_at" <= '2022-01-07 00:00:00')
実際に検索された条件は**「’2022-01-01 00:00:00′ から ‘2022-01-07 00:00:00まで」**。
つまり、「2022/01/07」の前日分(2022/01/06)までしか検索されていないことになっている。
欲しいのは、**2022/01/07 23:59:59まで**のが必要なのでこれでは取得できない。
欲しい日の **23:59:59まで**が取得できるように、predicate(述語)をカスタムする必要がある。
predicate(述語)をカスタム
config/initializers/ransack.rbを作成し、以下を記述していく。
Ransack.configure do |config|
config.add_predicate 'lteq_end_of_day',
arel_predicate: 'lteq',
formatter: proc { |v| v.end_of_day }
end
・config.add_predicate
述語の名前を定義
・arel_predicate
カスタムの元にするpredicateを指定
・formatter: proc { |v| v.end_of_day }
end_of_dayメソッドを実行している。
・proc
ブロックをオブジェクト化する働きがある。この場合だと、
{ |v| v.end_of_day }ここの処理をひとかたまりとしてprocに落とし込んでいる。
・end_of_day
end_of_dayメソッドは、その日の最後の時点 (23:59:59) のタイムスタンプを返す。
predicate(述語)を設定したら、先程のコードを修正すると、**「’2022-01-01 00:00:00′ から ‘2022-01-07 23:59:59まで」**のデータが抽出される。
<%= search_form_for @search, url: admin_users_path do |f| %>
<%= f.date_field :created_at_gteq%>
<span>~</span>
<%= f.date_field :created_at_lteq_end_of_day%>
<%end%>
【実装】プルダウン選択
<%= search_form_for @search, url: admin_users_path do |f| %>
<%= f.select :role_eq, User.roles_i18n.invert.map{|key, value| [key, User.roles[value]]}, { include_blank: "指定なし" }%>
<%end%>
# f.selectの基本形
<%= f.select :保存されるカラム名, [ ["表示される文字","保存される値"], ["表示される文字","保存される値"], {オプション}, {htmlオプション} ] %>
・User.roles_i18n.invert
User.roles_i18nによって{"general" => "一般", "admin" => "管理者" }というハッシュを取得している。
しかしf.selectの基本の形を見てみると、["表示される文字","保存される値"]となっているため、
.invertを使い、{"一般" => "general", "管理者" => "admin" }に変換する。
・.map{|key, value| [key, User.roles[value]]}
mapメソッドを使ってハッシュの要素の数だけ、ブロック内で処理を繰り返して新しい配列を返している。
ransackはenumで定義した文字列に対応しておらず、保存される値のvalueを0や1などのintegerで渡す必要がある。その際にUser.roles[value]が役立つ。
・include_blank
include_blankオプションは、先頭に表示されるメッセージに空白行を表示するオプション。
参考記事
[Rails] ransackを使った日時範囲検索
[Rails] enum_helpよる多言語対応とransackを使ってプルダウン検索機能を実装

