【问题标题】:Search by availability/dates Ruby On Rails按可用性/日期搜索 Ruby On Rails
【发布时间】:2019-09-18 13:03:44
【问题描述】:

我已经构建了一个 RoR 应用并实现了一个简单的预订系统。用户能够寻找空间并且可以每天或每小时预订它。 一切正常,但我现在想让用户能够根据其可用性寻找空间。 我希望用户能够选择开始/结束日期和开始/结束时间,并仅显示在此期间不包含任何预订的空间。

我目前正在使用 pg 搜索按类别和位置查找空间,但我不知道如何实现按日期和时间搜索,因为它使用不同的逻辑。 我试图通过为每个空间创建一系列预订来手动完成,以便我可以将其与参数进行比较,但它听起来非常复杂且不干净(无论如何我开始被卡住,因为它可以使用一个小时或几个小时或几天使其变得更加复杂)

有没有可以为我做到这一点的宝石?如果我必须手工完成,最好的开始方式是什么?

非常感谢

【问题讨论】:

    标签: ruby-on-rails ruby search


    【解决方案1】:

    只需创建一个实例方法available? 来测试没有与从到范围重叠的预订。您可以在关系上使用none?

    class Space
      has_many :bookings
      def available?(from, to)
        bookings.where('start_booking <= ? AND end_booking >= ?', to, from).none?
      end
    end
    

    【讨论】:

    • 是的,我正在为此苦苦挣扎。 @3limin4t0r ...我不想说empty?,而正确的表达方式让我忘记了。
    • 我觉得用exists比较好?方法,因为 SQL 查询性能更好,并且不会加载整个记录。
    • @Uelb 是的,你是对的。我懒惰的研究让我认为none? 是一个 SQL 查询,但它是一个枚举器。
    • @SteveTurczyn records.none? 没有块执行与!records.exists?相同的查询如果您检查none?的来源,您会发现它只是转发到empty?如果当前集合尚未加载,则依次调用!exists?。如果您确实向 #none? 提供了一个块,则使用 Enumerable 版本(加载所有记录数据)。
    • @3limin4t0r 哈哈!在我失去生存的意志之前,我会再次改变它。
    【解决方案2】:

    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 有一个startend 属性(类似于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),结果将会出现偏差。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-18
      • 1970-01-01
      • 1970-01-01
      • 2011-09-28
      • 2012-07-21
      • 2010-11-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多