##前提
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を使ってプルダウン検索機能を実装