如何检查两个日期之间的预订是否可行

时间:2016-04-05 20:57:30

标签: ruby-on-rails ruby date activerecord

Offer的数量为places。这意味着每天places可以为此优惠预订。 Offer has_many次预订。

Booking有一个date_begin,一个date_endbelongs_to一个Offer。这意味着在date_begindate_end之间的每一天都会使用一个关于相关Offer的地方。

如果要求预订优惠,我该如何检查是否可以验证?

示例:

- offer has 2 places (offer.places == 2).
- offer has currently 3 bookings (offer.bookings.count == 3)
- First booking B1 is between 01-04-2016 and 10-04-2016
- Second booking B2 is between 01-04-2016 and 05-04-2016
- Third booking B3 is between 08-04-2016 and 10-04-2016.

offer.available_between?("01-04-2016", "10-04-2016")
=> false (because of B1 and B2, and because of B1 and B3)

offer.available_between?("01-04-2016", "05-04-2016")
=> false (because of B1 and B2)

offer.available_between?("08-04-2016", "10-04-2016")
=> false (because of B1 and B3)

offer.available_between?("06-04-2016", "07-04-2016")
=> true (because there is only B1 during this period)

这是一个尝试:

class Offer < ActiveRecord::Base

  # True if and only if each day between date_begin and date_end has at least one place left.
  def available_between?(date_begin, date_end)
    (date_begin.to_datetime.to_i .. date_end.to_datetime.to_i).step(1.day) do |date|
      day = Time.at(date)
      nb_places_taken_this_day = self.bookings.where("date_begin <= :date AND date_end >= :date", date: day).count
      return false if nb_places_taken_this_day >= self.places
    end
    true
  end

end

offer.available_between?(booking.date_begin, booking.date_end)

我对这一点感到不舒服,特别是因为有多个独立的SQL查询。

您能否更有效地使用ActiveRecord看到更好的方法来实现这一目标?

1 个答案:

答案 0 :(得分:1)

我没有足够的数据来完整地测试这一点,但我认为这样的事情可以起作用。在这里,我建立了一个条件,即不存在预订,其中开始日期和结束日期不是在请求日期之前或之后。也就是说,确实存在的每个预订最好在请求的开始之前开始和结束,或者在请求结束之后开始和结束。如果预订不符合此条件,则所请求的日期不能作为单个预订提供。这有用吗?

[编辑 - 从原来的破解答案修改以反映实际问题]

这个新的答案仍然会持续几天,但是在一个查询中抓住所有冲突的预订之后这样做了。红宝石循环本身会非常快。

(注意:all?方法将立即在第一个false值上返回false,如果所有元素的值都为true,则返回true,因此我认为编辑所建议的添加return false unless是多余的。无论如何,我已经在最后一行添加了return,希望澄清意图。)

  def available_between?(date_begin, date_end)
    conflicting_bookings = self.bookings.where.not("(date_begin < :requested_start_date AND date_end < :requested_start_date) OR (date_end > :requested_end_date AND date_end > :requested_end_date)", requested_start_date: date_begin, requested_end_date: date_end)
    return (date_begin..date_end).all? do |day|
      num_bookings_for_day = conflicting_bookings.select{|booking| booking.date_begin <= day && booking.date_end >= day}.count
      num_bookings_for_day < self.places
    end
  end