環境
Rails v5.2.3
ruby v2.6.3p62
gem
fullcalendar-rails v3.9
テーブル構造概要
どうやって実現しよう...
modelにカスタムバリデーションを追加してエラーにしたい。
(controllerで行うと責務が増えすぎる気もした)
バリデーションでDBに保存済みの時間と画面から入力された時間を比較し、件数が0件か1件以上あるかで実装を頑張る。
条件としては
DBに保存済みの開始時間<入力された開始時間or入力された終了時間<DBに保存されている終了時間
にエラーとするべきと考えた。
⇨SQLでの抽出条件:
"DBに保存済みの開始時間 < 画面から入力された開始時間
AND 画面から入力された開始時間 < DBに保存されている終了時間
OR DBに保存済みの開始時間 < 画面から入力された終了時間
AND 画面から入力された終了時間 < DBに保存されている終了時間"
実装したソース
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
# アクセサ
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
##実装後
抽出条件が足りない模様。
入力された開始時間<DBに保存済みの開始時間〜終了時間<入力された終了時間
⇨SQLでの抽出条件:
"画面から入力された開始時間 < DBに保存済みの開始時間
AND 画面から入力された終了時間 < DBに保存されている終了時間"
##完成版ソース
# 画面から受け取った開始時間、終了時間をもとにすでに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
##感想
とりあえず実装はできたので良いが、結構処理が複雑になっているように見えるので、もっとスリムにしたいです。他に方法があったらご指摘ください