はじめに
掲示板一覧画面に、作成日を「◯◯月◯◯日〜◯◯月◯◯日」のような感じで日時範囲検索できるような機能を実装していきます。
前提としてgemのransack
は導入してあるものとします。
作業の流れ
- コントローラの作成
- ransackの検索条件を調べる
- 検索フォームに実装する
コントローラの作成
def index
@search = Board.ransack(params[:q])
@boards = @search.result(distinct: true).includes(:user).order(created_at: :desc).page(params[:page])
end
使えそうなransackの検索条件を調べる
今回は「◯◯月◯◯日〜◯◯月◯◯日」のような検索条件を利用したいです。どのようにしたらよいでしょうか?
まず実現可能なpredicateが用意されているか確認します。
predicateとは
ransackを使った検索フォームで使用できる検索述語のことです。
例えばcont
やeq
のようなものです。
公式wikiにまとめられています。こちらから今回の実装にあたって使えそうなものを探しましょう。
既存のpredicate _gteq と_lteq
使えそうなpredicateが2つ見つかりました。
_gteq
greater than or equal(より大きいか等しい)
_lteq
less than or equal(より小さいまたは等しい)
≤
と≥
のように使えそうですね。さっそくこちらを使って実装していきましょう!
検索フォームに実装する
<%= search_form_for @search, url: admin_boards_path do |f| %>
<div class="row">
<div class="form-inline align-items-center mx-auto">
<div class="col-auto">
<%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: t('defaults.search_word') %>
</div>
<div class="col-auto">
<%= f.date_field :created_at_gteq, class: 'form-control' %>
<span>~</span>
<%= f.date_field :created_at_lteq, class: 'form-control' %>
</div>
<div class="col-auto">
<%= f.submit class: 'btn btn-primary' %>
</div>
</div>
</div>
<% end %>
f.date_field
は日付の入力欄を生成させます。
画像のように検索条件を「2021/10/01 ~ 2021/10/06」としました。

しかし「2021/10/06」に作成された掲示板が表示されませんでした。おかしいですね。
発行されたSQLを見ていきます。
(0.1ms) SELECT COUNT(DISTINCT "boards"."id") FROM "boards" WHERE ("boards"."created_at" >= '2021-10-01 00:00:00' AND "boards"."created_at" <= '2021-10-06 00:00:00')
どうやら実際に検索された条件は「’2021-10-01 00:00:00′ から ‘2021-10-06 00:00:00まで」だったようです。
つまり、「2021/10/06」の前日分(2021/10/05)までしか検索されていなかったのです!
ransackの検索条件をカスタム
さきほどの問題を解決すべく、ransackのpredicateをカスタムしましょう。公式wikiを参考にします。
Ransack.configure do |config|
config.add_predicate 'equals_diddly',# 述語に名前をつけます。
# どんな非複合ARel述語を使うのか?(eq, matches, etc.)
arel_predicate: 'eq',
# 入力される値を好きなようにフォーマットします。(デフォルト: フォーマットを行わない)
formatter: proc { |v| "#{v}-diddly" },
end
config/initializars/ransack.rb
に設定しましょう。ファイルが無ければ、作成してください。
Ransack.configure do |config|
config.add_predicate 'lteq_end_of_day', # lteq_end_of_dayという述語の名前をつける
arel_predicate: 'lteq', # カスタムの元にするpredicateを指定
formatter: proc { |v| v.end_of_day } # ここでend_of_dayメソッドを実行している
end
arel_predicate:
でカスタムする述語を指定します。proc
とはなにか?
ざっくり言えば、ブロックをオブジェクト化するのがprocです。
今回で言えば、{ |v| v.end_of_day }
このブロックの処理をひとかたまりとしてprocに落とし込んでいるのです。
参考:https://docs.ruby-lang.org/ja/latest/class/Proc.html
- end_of_dayメソッド
end_of_day
メソッドは、その日の最後の時点 (23:59:59) のタイムスタンプを返します。
参考:https://railsguides.jp/active_support_core_extensions.html#beginning-of-day、end-of-day
irb(main):001:0> now = Time.current
=> Sun, 17 Oct 2021 18:51:21 JST +09:00
irb(main):002:0> now = Time.current.end_of_day
=> Sun, 17 Oct 2021 23:59:59 JST +09:00
検索フォームにカスタムしたpredicateを実装する
気を取り直して再び検索フォームを開いてカスタムしたpredicateを実装するように修正しましょう。
<%= search_form_for @search, url: admin_boards_path do |f| %>
<div class="row">
<div class="form-inline align-items-center mx-auto">
<div class="col-auto">
<%= f.search_field :title_or_body_cont, class: 'form-control', placeholder: t('defaults.search_word') %>
</div>
<div class="col-auto">
<%= f.date_field :created_at_gteq, class: 'form-control' %>
<span>~</span>
<%= f.date_field :created_at_lteq_end_of_day, class: 'form-control' %>
</div>
<div class="col-auto">
<%= f.submit class: 'btn btn-primary' %>
</div>
</div>
</div>
<% end %>
次はしっかり期待通りの挙動が得られました。発行されたSQLを見ておきましょう。
(0.2ms) SELECT COUNT(DISTINCT "boards"."id") FROM "boards" WHERE ("boards"."created_at" >= '2021-10-01 00:00:00' AND "boards"."created_at" <= '2021-10-06 23:59:59.999999')
コメント