LoginSignup
3
2

More than 3 years have passed since last update.

カレンダーの上で同一の時間帯のタスクの登録/更新をできないようにする

Posted at

何がしたいか

このような状況を無くしたい
スクリーンショット 2019-09-29 16.24.30.png

目指す姿😇
スクリーンショット 2019-09-29 16.27.36.png

環境

Rails v5.2.3
ruby v2.6.3p62

gem
fullcalendar-rails v3.9

テーブル構造概要

スクリーンショット 2019-09-29 16.36.06.png

どうやって実現しよう...

modelにカスタムバリデーションを追加してエラーにしたい。
(controllerで行うと責務が増えすぎる気もした)
バリデーションでDBに保存済みの時間と画面から入力された時間を比較し、件数が0件か1件以上あるかで実装を頑張る。

条件としては
DBに保存済みの開始時間<入力された開始時間or入力された終了時間<DBに保存されている終了時間
にエラーとするべきと考えた。
⇨SQLでの抽出条件:
"DBに保存済みの開始時間 < 画面から入力された開始時間
AND 画面から入力された開始時間 < DBに保存されている終了時間
OR DBに保存済みの開始時間 < 画面から入力された終了時間
AND 画面から入力された終了時間 < DBに保存されている終了時間"

実装したソース

controller.rb
def create #新規登録
  act_history = current_user.activity_historys.build(
    activity_name:      @json_hash[:activity_name],
    from_time:          @json_hash[:from_time],
    to_time:            @json_hash[:to_time],
    remarks:            @json_hash[:remarks],
    action:             "create",      #アクセサにセット
    current_user:       current_user   #アクセサにセット
  )

...(略)
end

def update #更新
  ...(略)
  respond_to do |format|
    if act_history.update(
        activity_name:      @json_hash[:activity_name],
        from_time:          @json_hash[:from_time],
        to_time:            @json_hash[:to_time],
        remarks:            @json_hash[:remarks],
        action:             "update",       #アクセサにセット
        current_user:       current_user    #アクセサにセット
      )
  ...(略)
end
model.rb
  # アクセサ
  attr_accessor :action, :current_user
  # バリデーション
  validate :check_time

 ...(略)

 private

  def check_time
    ...(略)
    same_time_zone_exists
  end
  ...(略)

 # 画面から受け取った開始時間、終了時間をもとにすでにDBに存在しているか検証する
  def same_time_zone_exists
    exists_act_historys = current_user.activity_historys.where(
      #SQL抽出条件
      "(from_time < ? AND ? < to_time OR from_time < ? AND ? < to_time)",
      from_time,
      from_time,
      to_time,
      to_time
    )


    if action == "create" && exists_act_historys.count > 0
      errors.add(:from_time, ": 同一時間帯の登録はできません")
      errors.add(:to_time, ": 同一時間帯の登録はできません")
    elsif action == "update" && exists_act_historys.count > 0
      exists_act_historys.pluck(:id).each do |exists_ids|
        if exists_ids != id
          errors.add(:from_time, ": 同一時間帯の登録はできません")
          errors.add(:to_time, ": 同一時間帯の登録はできません")
        end
      end
    end
  end

実装後

スクリーンショット 2019-09-29 18.04.07.png
⬇️エラーになるかテストその1
スクリーンショット 2019-09-29 18.05.35.png
⬇️正しくエラーになる!
スクリーンショット 2019-09-29 18.06.26.png

スクリーンショット 2019-09-29 18.04.07.png
⬇️エラーになるかテストその2
スクリーンショット 2019-09-29 18.07.56.png
⬇️エラーにならない...
スクリーンショット 2019-09-29 18.08.51.png

抽出条件が足りない模様。
入力された開始時間<DBに保存済みの開始時間〜終了時間<入力された終了時間
⇨SQLでの抽出条件:
"画面から入力された開始時間 < DBに保存済みの開始時間
AND 画面から入力された終了時間 < DBに保存されている終了時間"

完成版ソース

model.rb
# 画面から受け取った開始時間、終了時間をもとにすでにDBに存在しているか検証する
  def same_time_zone_exists
    exists_act_historys = current_user.activity_historys.where(
      #抽出条件追加
      "(from_time < ? AND ? < to_time OR from_time < ? AND ? < to_time) OR
       (? < from_time AND to_time < ?)",
      from_time,
      from_time,
      to_time,
      to_time,
      from_time,
      to_time
    )

    if action == "create" && exists_act_historys.count > 0
      errors.add(:from_time, ": 同一時間帯の登録はできません")
      errors.add(:to_time, ": 同一時間帯の登録はできません")
    elsif action == "update" && exists_act_historys.count > 0
      exists_act_historys.pluck(:id).each do |exists_ids|
        if exists_ids != id
          errors.add(:from_time, ": 同一時間帯の登録はできません")
          errors.add(:to_time, ": 同一時間帯の登録はできません")
        end
      end
    end
  end

スクリーンショット 2019-09-29 18.04.07.png
⬇️テスト再挑戦
スクリーンショット 2019-09-29 18.07.56.png
⬇️正しくエラーになることを確認
スクリーンショット 2019-09-29 18.16.06.png

感想

とりあえず実装はできたので良いが、結構処理が複雑になっているように見えるので、もっとスリムにしたいです。他に方法があったらご指摘ください

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2