日历中的重复事件 - Rails

Posted

技术标签:

【中文标题】日历中的重复事件 - Rails【英文标题】:Recurring Events in Calendar - Rails 【发布时间】:2012-04-26 07:08:24 【问题描述】:

我正在寻找对重复事件建模的最佳方法。我正在使用 fullcalendar 来显示事件。但我想最好在 Rails 后端处理重复事件。

我已经查看了其他问题和现有示例代码,但没有找到合适的。

它的行为应该类似于谷歌日历。所以应该可以删除/修改重复事件系列的单个事件。但是将事件系列的所有事件保存在数据库中似乎效率低下。此外,应该可以创建单个事件而不会重复发生。

什么是好的模型架构?

我的事件模型现在看起来像这样(没有额外的属性):

# Table name: events
#
#  id              :integer         not null, primary key
#  employee_id     :integer
#  created_at      :datetime
#  updated_at      :datetime
#  starts_at       :datetime
#  ends_at         :datetime
#

class Event < ActiveRecord::Base
  attr_accessible :starts_at, :ends_at
end

【问题讨论】:

【参考方案1】:

我对 Rails 很陌生,您的解决方案听起来很有趣。要创建计划和相关事件,您是否在 Event 模型中使用条件回调?

在我的例子中,用户可以创建事件,无论是否每周重复。所以我在考虑事件模型中的一个循环布尔字段。所以我猜你会有第一个回调来创建时间表:

before_save :create_weekly_schedule, if: :recurring

基本上是第二个来创建事件:

after_save :create_occurences_if_recurring

def create_occurences_if_recurring
  schedules.each do |sched|
    occurences.create(start_date: sched.start_time, end_date: sched.end_time)
  end
end

您的解决方案听起来合乎逻辑吗? 谢谢

【讨论】:

我看到你在回复my answer。当您回复特定答案时,您应该对该答案发表评论,否则将不会通知作者。另外,如果您的问题很长(例如这个问题),请不要将其放在原始问题的另一个答案中;在网站上创建一个新问题,链接到您要回复的答案。 至于我对您的解决方案的看法,我发现它有问题。你说schedules.each——但如果时间表只是“每周重复一次”,那么each 循环将永远持续下去,因为时间表没有指定结束日期。这就是为什么我没有提前生成所有事件的原因——它们的数量是无限的。【参考方案2】:

就我而言,我做了这样的事情:

# Holds most of my event's data; name, description, price ...
class Event < ActiveRecord::Base
  has_many :schedules
  has_many :occurrences
  attr_accessible :started_at, :expired_at # expired_at is optional
end

# Holds my schedule object
class Schedule < ActiveRecord::Base
  belongs_to :event
  attr_accessible :ice_cube_rule # which returns my deserialized ice_cube object
end

# Holds generated or manually created event occurrences 
class Occurrence < ActiveRecord::Base
  belongs_to :event
  attr_accessible :started_at, :expired_at
  attr_accessible :generated # helps me tell which occurrences are out of an ice_cube generated serie
  attr_accessible :canceled_at
end

从那里,我使用ice_cube 管理出现次数计算并将结果存储在出现次数表中。我首先尝试在不使用 Occurrence 模型的情况下工作,但无论规则引擎多么先进,总会有例外情况,因此将出现次数存储在它们自己的模型中会给您带来灵活性。

使用 Occurrence 模型可以更轻松地在日历上显示事件或使用日期搜索过滤器,因为您只需要查询事件,然后显示相关事件的数据,而不是收集给定日期范围内的所有事件然后必须过滤掉时间表不匹配的事件。

您还可以将事件发生标记为已取消或对其进行修改(将生成的属性设置为 false,以便在编辑 ice_cube 时间表时不会清理它...或您的业务需要的任何内容)

当然,如果您有无限期重复的事件,您需要限制希望在未来多长时间内生成这些事件,并使用自动 rake 任务清理旧事件并生成明年的事件或所以。

到目前为止,这种模式对我来说效果很好。

另外,看看recurring_select gem,它是一个非常简洁的 ice_cube 表单输入。

【讨论】:

好帖子。很好奇您对这种方法的想法:blog.plataformatec.com.br/2010/04/recurring-events 看起来和您的相似。 Bruno,你提到的方法是有效的,只要你不需要坚持这些重复的状态或处理异常(即:音乐会代表被推迟到第二天,以防下雨) @Jim 你有我可以弄乱的演示应用程序或示例应用程序吗?我喜欢你的解决方案 @Frank004 不,我很抱歉,但没有比这个 sn-p 更多的东西了。剩下的就是你自己的业务逻辑了。一年多前我实施了这项技术,现在它仍然有效。 @Jim 谢谢你 :)【参考方案3】:

这是我将如何建模。我没有太多使用 Google 日历,所以我将功能基于 iCal 的重复事件。

所有模型都应具有通常的 id、created_at、updated_at 属性。列出的是自定义属性。如果该属性是另一个模型,则将其实现为关联,例如 has_onebelongs_to

RecurrencePeriod Event base_event # has_one :base_event, :class_name'Event' Time end_date # 可能是 nil,如果它永远重复的话 WeeklyRecurrence 重复# has_one :recurrence, :as=&gt;:recurrence Array[OccurrenceOverride] 覆盖 # has_many :overrides, :class_name=&gt;'OccurrenceOverride'

RecurrencePeriod 在其 base_event 开始的日期开始。另外,我假设Event 的employee_id 指的是创建该事件的员工。 RecurrencePeriod 也将属于创建 base_event 的员工。

模型取决于您希望能够灵活地指定重复次数。您是要支持“每周二和周四,从上午 10 点到上午 11 点,从下午 2 点到下午 3 点”,还是只支持“每周重复一次”?这是一个只支持“每周重复”、“每两周重复”等的模型;如果需要,您可以扩展它。

WeeklyRecurrence Integerweek_between_recurrences RecurrencePeriodrecurrence_period # belongs_to :recurrence, :polymorphic=&gt;true

我在这里使用polymorphic associations,因为我认为如果您想要不止一种重复类型,例如WeeklyRecurrenceDailyRecurrence,它们可能会很有用。但我不确定它们是否是正确的建模方法,所以如果结果不是这样,请改用 has_one :weekly_recurrencebelongs_to :recurrence_period

Ice cube 库似乎对计算重复率很有用。如果上面的WeeklyRecurrence 不够强大,您可能只想在模型中存储一个冰块Schedule 对象,替换WeeklyRecurrence。要将Schedule对象存储在模型中,将其保存为属性“schedule”,将serialize :schedule放入模型定义中,并在数据库中生成文本列“schedule”。

OccurrenceOverride 处理正在编辑重复事件的单个实例的情况。

OccurrenceOverride RecurrencePeriodrecurrence_period_to_override # belongs_to :recurrence_period_to_override, :class_name=&gt;'RecurrencePeriod' Time original_start_time #唯一标识要替换该 RecurrencePeriod 中的哪个重复 Eventreplacement_event # has_one :replacement_event, :class_name=&gt;'Event';可能为零,如果该重复被删除而不是编辑

不要单独存储每个事件的发生,而是在需要在视图中显示它们时临时生成它们。在RecurrencePeriod 中,创建一个方法generate_events_in_range(start_date, end_date) 生成Events,而不是保存在数据库中,而只是传递给视图以便它可以显示它们。

当用户编辑重复发生时,他们应该可以选择修改所有事件、所有未来事件或仅修改该事件。如果他们修改了所有事件,请修改 RecurrencePeriod 的 base_event。如果他们修改了所有未来发生的事件,请使用您应该在RecurrencePeriod 上实施的方法,该方法在某个日期的任一侧将自身拆分为两个RecurrencePeriods,然后将更改保存到第二个期间。如果他们只修改该事件,请为他们覆盖的时间创建一个OccurrenceOverride,并将更改保存到覆盖的替换事件。

当用户说某个事件现在应该在可预见的未来每两周重复一次时,您应该创建一个新的RecurrencePeriod,将该事件作为 base_event 和一个 nil end_date。它的重复周期应该是一个新的WeeklyRecurrence,并且weeks_between_recurrence=2,它应该没有OccurrenceOverrides。

【讨论】:

在无法通过 id 访问它的情况下没有持续发生的任何问题? (性能、复杂性等) @ted 好问题。我从来没有实现过这个,所以恐怕我不知道根据需要生成事件是否会导致任何问题。我想您可以在必要时为生成的事件设置缓存。缓存系统可能会使用这样一个事实,即生成的要保存的事件在概念上与OccurrenceOverride 非常相似,但并不完全相同。【参考方案4】:

只是一个想法,也许评论者会指出我暂时没有想到的问题:

我会制作一个RecurringEvent 模型(或任何你想叫它的名称)has_many :events

假设每个事件都是由员工创建的(基于您的笔记),那么RecurringEvent 也将是belong_to :employee。然后,您可以建立has_many :through 关系,其中员工有很多事件并且有很多重复事件。

RecurringEvent 模型可以有一个开始日期和一个模式,它最初可以使用这个模式来创建各个发生的事件。然后,对于属于重复系列的任何事件,您可以修改或删除该单个事件,但您也可以“重新生成系列”,删除系列中的所有事件(或系列中的所有未来事件)并重建它们基于一种新的模式,例如将会议从“每个星期二”更改为“每个星期四”。

另外一种好处是,您可以创建一个重复事件的一览表,这可能让您对人们的主要义务有一些很好的了解。

就像我说的那样,我想这就是我的处理方法,但这只是一个想法,我还没有构建任何类似的东西,所以我不知道是否有任何大的陷阱 在我建议的方法中。

祝你好运,请发布你最终做了什么!

【讨论】:

以上是关于日历中的重复事件 - Rails的主要内容,如果未能解决你的问题,请参考以下文章

Google Script:将工作时间数据记录到日历中的事件[重复]

乘客中的重复事件,在 websocket-rails 中

重复事件、SQL 查询

如何每 x 秒重复 ICS 文件中的事件?

日历重复/重复事件 - 最佳存储方法

从电子表格创建 Google 日历事件但防止重复