【ransack】日付選択検索とプルダウン選択

##完成イメージ図
**日付選択検索**
[![Image from Gyazo](https://i.gyazo.com/e7271a685b49647b0f5d775fa954a0a3.png)](https://gyazo.com/e7271a685b49647b0f5d775fa954a0a3)

**プルダウン選択**
[![Image from Gyazo](https://i.gyazo.com/7bc63b12affad17e0a4a7b301f3314c7.gif)](https://gyazo.com/7bc63b12affad17e0a4a7b301f3314c7)

##前提
``ransack``と``enum/enum_help``の導入や設定をしていること。

##【実装】日付選択検索
「◯◯月◯◯日〜◯◯月◯◯日」のようにするため、``predicate（述語）``を使っていく。

``predicate（述語）``とは``ransack``を使った検索フォームで使用できる検索述語のこと。``cont（部分一致）``や``_eq（イコール）``に該当する。

「◯◯月◯◯日〜◯◯月◯◯日」を文章で表すと、
「◯◯月◯◯日 **以上** ◯◯月◯◯日 **以下** 」、つまり記号で表すと
「◯◯月◯◯日 ``≤`` ``≥``◯◯月◯◯日  」になる。

``≤``　はより大きいか等しい、述語だと**``_gteq``**に該当する。
``≥``　はより小さいか等しい、述語だと**``_lteq``**に該当する。

**``_gteq``**と**``_lteq``**を使ってコードに表記してみる。

```search.html.rb
<%= 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%>
```

しかし上記のコードで、１月１日〜１月７日まで検索をかけても、１月７日分は表示されない。
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``を作成し、以下を記述していく。

```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.html.rb
<%= 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.html.rb
<%= 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を使った日時範囲検索](https://osamudaira.com/286/)
[[Rails] enum_helpよる多言語対応とransackを使ってプルダウン検索機能を実装](https://osamudaira.com/274/)