ransackで入力した値が、営業時間内か検索する。

#はじめに
現在デートスポットのレビューサイトを作成中です。
検索した時間が、営業時間内に入っていれば表示する検索機能を実装しようとしたら、なかなか実装できませんでした。
その実装方法を記事にしてみようと思います。

#modelの概要
```
create_table "date_spots", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.integer "genre_id"
    t.string "name"
    t.datetime "opening_time"
    t.datetime "closing_time"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
  end
```
始業時間(opening_time)と終業時間(closing_time)がデートスポットごとに決められています。
私が実装したい検索機能は、入力した時間が、openinng_timeとclosing_timeの間ならば検索できるというものです。

#viewの概要
検索フォームのviewのコードになります。

```
<div>
  <%= search_form_for @date_spot_search_params, as: :date_spot_search  do |f| %>
    <%= f.collection_select :opening_time_lteq, BusinessTime.all, :value_time, :time, {selected: @date_spot_search_params.opening_time_lteq.to_s, prompt: "来店時間"} %>
    <%= f.submit '検索' %>
  <% end %>
</div>
```

BusinessTimeAllはActiveHashで作成した時間の文字列になっています。
朝6:00から、次の日の深夜5時まで時間が登録されています。
一部分を抜粋して表示します。

```
    {time: '00:00', value_time: '2000-01-02 00:00:00 UTC'}, {time: '00:30', value_time: '2000-01-02 00:30:00 UTC'},
    {time: '01:00', value_time: '2000-01-02 01:00:00 UTC'}, {time: '02:00', value_time: '2000-01-02 02:00:00 UTC'},
    {time: '03:00', value_time: '2000-01-02 03:00:00 UTC'}, {time: '04:00', value_time: '2000-01-02 04:00:00 UTC'}
```
timeカラムはセレクトボックスに表示するために用意されたカラムで、value_timeは値として使用するために用意したカラムです。
value_timeで注目していただきたいのが、00:00時以上になると日付を変化させている点です。
日付を変化させることで、時系列を明確にしています。
このアプリケーションの営業時間カラムでは日付は表示しないので、日付は固定させています。

同じ画面にデートスポットモデル以外の検索機能も実装しているため、独自の検索キーを設定しています。

```
@date_spot_search_params = DateSpot.ransack(params[:date_spot_search], search_key: :date_spot_search)
```

これを踏まえて、viewの3行目のopening_time_lteqという部分に注目していただきたいです。

```
<%= f.collection_select :opening_time_lteq, BusinessTime.all, :value_time, :time, {selected: @date_spot_search_params.opening_time_lteq.to_s, prompt: "来店時間"} %>
```
これは、始業時間がtext_boxに入力した時間以下のデートスポットを全て検索するというマッチャになります。
例えば、8時から23時までの営業時間のデートスポットがあったとすると、text_boxに9時と入力すると、始業時間は8時なので検索されることになります。
始業時間以上のお店は検索できますが、閉店時間をすぎていても、このままだと検索されてしまします。
閉店時間以内に入力した時間が入っているかも同時に検索しなければなりません。
そこでコントローラで一工夫しました。

#controller概要
###application_controller
```
  include SessionsHelper
  include UsersHelper
  
  #デートスポットの名前検索の際に使用する
  def set_q_for_date_spot
    @date_spot_search_params = DateSpot.ransack(params[:date_spot_search], search_key: :date_spot_search)
  end

  #ユーザーの名前検索の際に使用する
  def set_q_for_user
    # 同時に１画面で同じパラメータを検索するため、違うパラメータを用意する。
    @user_search_params = User.ransack(params[:q])
  end
```

###date_spots_controller
```
before_action :set_q_for_date_spot

def index
    date_spot_search_params_decided = @date_spot_search_params.result.ransack(closing_time_gteq: @date_spot_search_params.opening_time_lteq)
    @date_spots = date_spot_search_params_decided.result
  end
```
date_spots_controllerの方で解説したいと思います。
まず、3行目にあるdate_spot_search_params_decidedは、application_controllerで定義した変数@date_spot_search_paramsを使用しています。
この変数にはformから送られてきた、値が格納されています。
viewの部分で説明した通り、formで入力された値以下の始業時間のデートスポットが全て格納されています。
ここからがポイントです。
そこからさらに入力された値以上の閉店時間のデートスポットを検索します。
そのためのコードがこちらになります。

```
@date_spot_search_params.result.ransack(closing_time_gteq: @date_spot_search_params.opening_time_lteq)
```
closing_time_gteqで対象の値以上の閉店時間を持つデートスポットを検索するようにします。
対象の時間は@date_spot_search_params.opening_time_lteqにformでviewで入力された値が入っています。
あとはdate_spot_params_decided.resultを行うだけで、求めていた結果が表示されました。

#まとめ

formに開始時間と終了時間を入力し、検索対象がその範囲に入っているかを検索する記事は多数発見しましたが、自分のように来店したい時間が営業時間の範囲に入っているか検索したい人の記事は少ないようでしたので、この記事を書きました。
まだまだ、エンジニアとしての日が浅く知識として不十分な部分がありますので、何か間違いがあるかもしれません。
間違いがあれば指摘していただけると幸いです。