ホテルの客室予約　予約する時の空室検索の実装

初めての投稿です！
プログラミングスクール３ヶ月目でPFで架空のホテルのホームページを制作しました。
予約する時の空室検索を実装するときに参考になる記事を見つけることができず苦労したので参考になればと思い共有させていただきます。
※本格的なものではありません！
![スクリーンショット 2021-03-18 18.25.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1020497/1e1aeb2b-4a0c-73be-e501-11519b7ccd9b.png)
※検索ページ↑
##仕組み
![スクリーンショット 2021-03-18 17.28.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1020497/1edb3e9c-89fb-ee85-67f8-1e6d5c034236.png)
>ホテルにはA、B、Cという部屋がある。
・Aタイプはホテルに３室、Bタイプはホテルに５室、Cタイプはホテルに１０室（部屋数：capacity）というように制限がある。
・それぞれの客室に定員数が決まっている（定員数：people）
>>・ユーザーがチェックイン日（start_date）とチェックアウト日（end_date）とpeople（定員数）を入力して検索し、宿泊できる部屋を一覧で表示するといった流れです。
:warning:連泊での予約はできない
:warning:客室からの空室検索はできない
といった点に注意してください。

##実装
>カラム名
start_date　チェックイン日（到着日）
end_date　チェックアウト日（出発日）
people　定員数

空室検索ページのビューです。

```html:search.html.erb
<%= form_with model: @reservation, url: room_reservations_path do |f| %>
<label for="reservation_start_date">チェックイン日</label><%= f.date_field :start_date, required: true %>   

<label for="reservation_end_date">チェックアウト日</label>
<%= f.date_field :end_date, required: true %>    

<label for="reservation_people">宿泊人数</label>
<%= f.select :people, [1,2,3,4] %>

<%= f.submit "検索", class: "button", id: "button" %>
<% end %>
```

form_withで選択された日付がreservationコントローラのroomに飛ぶように指定しているのでroomアクションに記述していきたいと思います↓

```ruby:reservation_controller.rb
class ReservationsController < ApplicationController
  def search
    @reservation = Reservation.new
  end

  def room
    @start_date = Date.parse(reservation_params[:start_date])
    @end_date = Date.parse(reservation_params[:end_date])

    reservations = Reservation.where(start_date: @start_date...@end_date).group(:room_id)

    not_available_room_ids = []
    reservations.each do |reservation|
      room_reservation = Reservation.where(start_date: @start_date...@end_date)
                                    .where(room_id: reservation.room.id)
      # 予約されてる部屋の件数 == 実際にある部屋数
      if room_reservation.count == reservation.room.capacity
        # 予約不可の部屋のID　<< 予約できない部屋のIDを配列に追加
        not_available_room_ids << reservation.room.id
      end
    end

    @people = (reservation_params[:people]).to_i
    room_all = Room.all
    room_all.each do |room|
      # 選択された人数 > 部屋の定員数
      if @people > room.people
        not_available_room_ids << room.id
      end
    end

    # .notで予約不可の部屋のID以外を＠roomsに代入
    @rooms = Room.where.not(id: not_available_room_ids)
  end
end
```
順を追って説明します。
まず1行目と2行目の@start_dateと@start_dateに選択された日付を取得し代入し、
reservationsには選択された日付の予約されている部屋をgrepで取得。
ここでは予約されている部屋の種類はどれかという情報だけ。

```ruby:reservation_controller.rb
@start_date = Date.parse(reservation_params[:start_date])
@end_date = Date.parse(reservation_params[:end_date])

reservations = Reservation.where(start_date: @start_date...@end_date).group(:room_id)
```
次に宿泊不可の部屋を入れるための空の配列を作成。

```ruby:reservation_controller.rb
not_available_room_ids = []
```

reservationsの情報をもとに選択された日付の予約されている部屋の予約をroom_reservationに代入する。
条件分岐で予約されている部屋の件数と実際にある部屋数（capasity）が一緒ならその部屋は満室ということなので、その部屋のIDを先ほど作った配列（not_available_room_ids）に入れる。

```ruby:reservation_controller.rb
reservations.each do |reservation|
      room_reservation = Reservation.where(start_date: @start_date...@end_date)
                                    .where(room_id: reservation.room.id)
      # 予約されてる部屋の件数 == 実際にある部屋数
      if room_reservation.count == reservation.room.capacity
        # 予約不可の部屋のID　<< 予約できない部屋のIDを配列に追加
        not_available_room_ids << reservation.room.id
      end
```

人数も同じように条件分岐で選択された人数と定員数を比べ、宿泊不可の部屋の配列へ入れています（例：定員数は2名なのに3名で選択されたら2名以下が定員数のの部屋は検索結果に表示されないようにする）

最後に、予約不可の部屋のIDの配列を使い、@roomsに宿泊できる部屋をwhereで抽出し、検索結果のページには@roomsをeach分で回して表示しています。

```ruby:reservation_controller.rb
@rooms = Room.where.not(id: not_available_room_ids)
```


他の実装方法もあると思うので参考までに！