从answer of SteveTurczyn 中获得一些灵感。以下内容可能会给您一些启发。
class Space < ApplicationRecord
# attributes: id
has_many :bookings
def self.available(period)
bookings = Booking.overlap(period)
where.not(id: bookings.select(:space_id))
end
def available?(period)
if bookings.loaded?
bookings.none? { |booking| booking.overlap?(period) }
else
bookings.overlap(period).none?
end
end
end
class Booking < ApplicationRecord
# attributes: id, space_id, start, end
belongs_to :space
def self.overlap(period)
period = FormatConverters.to_period(period)
# lteq = less than or equal to, gteq = greater than or equal to
# Other methods available on attributes can be found here:
# https://www.rubydoc.info/gems/arel/Arel/Attributes/Attribute
where(arel_table[:start].lteq(period.end).and(arel_table[:end].gteq(period.start)))
end
def overlap?(period)
period = FormatConverters.to_period(period)
self.start <= period.end && self.end >= period.start
end
module FormatConverters
module_function
def to_period(obj)
return obj if obj.respond_to?(:start) && obj.respond_to?(:end)
obj..obj
end
end
end
通过上述实现,您可以查询单个空间是否在一段时间内可用:
from = Time.new(2019, 10, 1, 9, 30)
to = Time.new(2019, 10, 5, 17, 30)
period = from..to
space.available?(period) # true/false
您可以获得所有可用空间:
spaces = Space.available(period) # all available spaces during the period
请注意,类方法也将在作用域链上可用:
spaces = Space.scope_01.scope_02.available(period)
我还添加了overlap 范围和overlap? 帮助器来简化上述帮助器的创建。
由于在我的版本中Booking 有一个start 和end 属性(类似于Range),您也可以将它提供给任何接受句点的方法。
booking_01.overlap?(booking_02) # true/false
要检索此时重叠的所有预订:
bookings = Booking.overlap(Time.now) # all bookings overlapping the period
希望这能给你一些启发。如果您想知道重叠检查的工作原理,我必须将您转至this 问题。
注意:此答案假定提供的时间段有效。又名start end。如果您出于某种原因提供Time.new(2019, 10, 1)..Time.new(2019, 9, 23),结果将会出现偏差。