25

Tôi đang tìm cách tốt nhất để lập mô hình các sự kiện định kỳ. Tôi đang sử dụng fullcalendar để hiển thị các sự kiện. Nhưng tôi đoán các sự kiện định kỳ được xử lý tốt nhất trên các phụ trợ đường ray.Sự kiện định kỳ trong Lịch - Đường ray

Tôi đã xem xét các câu hỏi khác và mã ví dụ hiện tại nhưng tôi không tìm thấy bất kỳ điều gì phù hợp.

Nó sẽ hoạt động tương tự như lịch google. Vì vậy, có thể xóa/sửa đổi các sự kiện đơn lẻ của chuỗi sự kiện lặp lại. Nhưng lưu tất cả các sự kiện của chuỗi sự kiện trong cơ sở dữ liệu có vẻ không hiệu quả. Ngoài ra, có thể tạo các sự kiện đơn lẻ mà không có bất kỳ sự lặp lại nào.

Kiến trúc mô hình tốt là gì?

mô hình sự kiện của tôi ngay bây giờ trông như thế (mà không thuộc tính bổ sung):

# 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 

Trả lời

37

Đây là cách tôi sẽ làm mẫu này. Tôi đã không sử dụng Lịch Google nhiều, vì vậy tôi đang dựa vào chức năng của các sự kiện lặp lại của iCal.

Tất cả các mô hình phải có thuộc tính id, created_at, updated_at thông thường. Được liệt kê là các thuộc tính tùy chỉnh. Nếu thuộc tính là một mô hình khác, bạn sẽ triển khai một liên kết như has_one hoặc belongs_to.

  • RecurrencePeriod
    • Event base_event # has_one :base_event, :class_name'Event'
    • Time end_date # có thể con số không, nếu nó tái mãi mãi
    • WeeklyRecurrence tái phát # has_one :recurrence, :as=>:recurrence
    • Array[OccurrenceOverride] ghi đè # has_many :overrides, :class_name=>'OccurrenceOverride'

Các RecurrencePeriod bắt đầu vào ngày base_event của nó bắt đầu. Ngoài ra, tôi giả định rằng employee_id của Event chỉ đến nhân viên đã tạo ra sự kiện đó. Một RecurrencePeriod cũng sẽ thuộc về nhân viên đã tạo base_event.

Mô hình phụ thuộc vào độ linh hoạt mà bạn muốn có thể chỉ định sự lặp lại. Bạn sẽ hỗ trợ "Thứ Ba và Thứ Năm cứ hai tuần một lần từ 10 giờ sáng đến 11 giờ sáng và từ 2 giờ chiều đến 3 giờ chiều" hay chỉ "lặp lại hàng tuần"? Dưới đây là một mô hình chỉ hỗ trợ "lặp lại hàng tuần", "lặp lại hai tuần một lần", v.v. bạn có thể mở rộng nó nếu bạn cần.

  • WeeklyRecurrence
    • Integer weeks_between_recurrences
    • RecurrencePeriod RECURRENCE_PERIOD # belongs_to :recurrence, :polymorphic=>true

tôi sử dụng polymorphic associations đây, bởi vì tôi nghĩ rằng họ có thể có ích nếu bạn muốn nhiều hơn một loại của sự tái diễn, chẳng hạn như cả WeeklyRecurrenceDailyRecurrence. Nhưng tôi không chắc chắn rằng đó là cách chính xác để mô hình hóa điều đó, vì vậy nếu chúng không thành công, chỉ cần sử dụng has_one :weekly_recurrencebelongs_to :recurrence_period thay thế.

Thư viện Ice cube có vẻ hữu ích khi tính toán các lần lặp lại. Nếu WeeklyRecurrence ở trên không đủ mạnh, bạn có thể chỉ muốn lưu trữ đối tượng Ice cube Schedule trong một mô hình, thay thế WeeklyRecurrence. Để lưu trữ một đối tượng Schedule trong một mô hình, hãy lưu đối tượng đó dưới dạng thuộc tính "lịch biểu", đặt serialize :schedule vào định nghĩa mô hình và tạo cột "lịch biểu" trong cơ sở dữ liệu.

OccurrenceOverride xử lý trường hợp một phiên bản duy nhất của sự kiện lặp lại đang được chỉnh sửa.

  • OccurrenceOverride
    • RecurrencePeriod recurrence_period_to_override # belongs_to :recurrence_period_to_override, :class_name=>'RecurrencePeriod'
    • Time original_start_time # xác định duy nhất mà tái phát trong vòng RecurrencePeriod rằng để thay thế
    • Event replacement_event # has_one :replacement_event, :class_name=>'Event'; có thể con số không, nếu tái phát mà đã bị xóa thay vì sửa

Thay vì lưu trữ mỗi lần xuất hiện của một sự kiện riêng lẻ, tạo cho họ tạm thời khi bạn cần phải cho họ thấy trong giao diện. Trong RecurrencePeriod, tạo phương thức generate_events_in_range(start_date, end_date) tạo ra Event s, không lưu vào cơ sở dữ liệu, nhưng chỉ để chuyển sang chế độ xem để có thể hiển thị chúng.

Khi người dùng chỉnh sửa lặp lại, họ phải có tùy chọn sửa đổi tất cả các lần xuất hiện, tất cả các lần xuất hiện trong tương lai hoặc chỉ sự kiện đó. Nếu họ sửa đổi tất cả các lần xuất hiện, hãy sửa đổi base_event của RecurrencePeriod. Nếu họ sửa đổi tất cả các lần xuất hiện trong tương lai, hãy sử dụng phương pháp bạn nên thực hiện trên RecurrencePeriod chia tách chính nó thành hai RecurrencePeriod s ở hai bên của một ngày nhất định, sau đó lưu các thay đổi chỉ vào giai đoạn thứ hai. Nếu họ chỉ sửa đổi sự kiện đó, hãy tạo một OccurrenceOverride cho thời gian chúng ghi đè và lưu các thay đổi đối với replace_event của ghi đè.

Khi người dùng nói một sự kiện nhất định sẽ tái diễn sau mỗi hai tuần trong tương lai gần, bạn nên tạo RecurrencePeriod mới với sự kiện đó là base_event và nil end_date. Sự lặp lại của nó phải là WeeklyRecurrence mới với tuần_between_recurrence = 2 và nó sẽ không có OccurrenceOverride s.

+1

Bất kỳ vấn đề nào từ trải nghiệm không có sự cố xảy ra liên tục mặc dù không thể truy cập bằng id? (hiệu suất, độ phức tạp, v.v.) – ted

+0

@ted Câu hỏi hay. Tôi chưa bao giờ triển khai điều này, vì vậy tôi e rằng tôi không biết liệu có xảy ra sự cố khi cần thiết hay không. Tôi cho rằng bạn có thể thiết lập bộ nhớ cache cho các lần xuất hiện được tạo nếu cần. Một hệ thống bộ nhớ đệm có thể sử dụng thực tế là một lần xuất hiện được tạo ra để lưu là khái niệm rất giống với một 'OccurrenceOverride', mặc dù không giống nhau. –

3

Chỉ cần một ý kiến ​​ra khỏi đỉnh đầu của tôi, có lẽ comenters sẽ chỉ ra một vấn đề tôi không nghĩ đến việc tại thời điểm này:

Tôi sẽ tạo mô hình RecurringEvent (hoặc bất kỳ điều gì bạn muốn gọi) has_many :events.

Giả sử mỗi sự kiện được tạo bởi một nhân viên (dựa trên ghi chú của bạn), sau đó RecurringEvent cũng sẽ là belong_to :employee. Sau đó, bạn có thể xây dựng mối quan hệ has_many :through trong đó nhân viên có nhiều sự kiện và có nhiều sự kiện lặp lại.

Mô hình RecurringEvent có thể có ngày bắt đầu và mẫu và ban đầu có thể sử dụng mẫu này để tạo sự kiện xảy ra riêng lẻ. Sau đó, trên bất kỳ sự kiện nào là một phần của chuỗi định kỳ, bạn có thể sửa đổi hoặc xóa sự kiện đó, nhưng bạn cũng có thể 'tạo lại chuỗi', xóa tất cả các sự kiện trong chuỗi (hoặc tất cả các sự kiện trong tương lai) và xây dựng lại chúng dựa trên một mẫu mới, ví dụ như di chuyển cuộc họp từ "mỗi thứ ba" đến "mỗi thứ năm".

Một điều tốt đẹp khác về điều này là bạn có thể tạo danh sách các sự kiện định kỳ, có thể cung cấp cho bạn một số thông tin chi tiết về nghĩa vụ chính của mọi người. Giống như tôi đã nói, ngoài đỉnh đầu của tôi đó là cách tôi sẽ tiếp cận nó, nhưng đây chỉ là một ý tưởng và tôi đã không xây dựng bất cứ điều gì như thế vì vậy tôi không biết nếu có bất kỳ lớn gotchas trong cách tiếp cận tôi gợi ý.

Chúc bạn may mắn, hãy đăng những gì bạn sẽ làm!

5

Trong trường hợp của tôi, tôi đã làm một cái gì đó như thế này:

# 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 

Từ đó, tôi đã sử dụng ice_cube để quản lý các tính xuất hiện và lưu trữ kết quả trong bảng xuất hiện. Lần đầu tiên tôi cố gắng làm việc mà không có mô hình Occurrence, nhưng dù công cụ quy tắc nâng cao, bạn sẽ luôn có ngoại lệ, vì vậy việc lưu trữ các lần xuất hiện trong mô hình của riêng chúng sẽ mang đến cho bạn sự linh hoạt.

Có mô hình Xuất hiện giúp hiển thị các sự kiện trên lịch hoặc bộ lọc tìm kiếm ngày dễ dàng hơn khi bạn chỉ cần truy vấn lần xuất hiện và sau đó hiển thị dữ liệu của sự kiện có liên quan thay vì thu thập tất cả các sự kiện trong một ngày cụ thể phạm vi và sau đó phải lọc ra các sự kiện mà lịch biểu không khớp.

Ngoài ra, bạn có thể đánh dấu một sự xuất hiện sự kiện như hủy bỏ hoặc sửa đổi nó (thiết lập các thuộc tính tạo ra tại sai vì vậy nó không được dọn dẹp khi chỉnh sửa một lịch trình ice_cube ... hoặc bất cứ nhu cầu kinh doanh của bạn là)

Trong số nếu bạn có các sự kiện lặp lại vô thời hạn, bạn sẽ muốn giới hạn khoảng cách trong tương lai mà bạn muốn những lần xuất hiện đó được tạo ra và sử dụng các tác vụ tự động cào để dọn sạch các tác vụ cũ và tạo ra các lần xuất hiện trong năm tới.

Cho đến nay mẫu này hoạt động khá tốt đối với tôi.

Ngoài ra, hãy xem đá quý recurring_select là hình thức đầu vào hình thức ice_cube khá gọn gàng.

+2

Bài đăng hay. Tò mò về suy nghĩ của bạn với cách tiếp cận này: http://blog.plataformatec.com.br/2010/04/recurring-events Hình như tương tự như của bạn. – Bruno

+0

Bruno, cách tiếp cận mà bạn đề cập là hợp lệ miễn là bạn không cần phải duy trì trạng thái lặp lại hoặc xử lý ngoại lệ (ví dụ: buổi hòa nhạc được chuyển sang ngày hôm sau trong trường hợp mưa) – Jim

+0

@Jim Bạn có bản trình diễn không ứng dụng hoặc ứng dụng ví dụ mà tôi có thể gây rối với nó? Tôi thích bạn giải pháp – Frank004

-1

Tôi rất mới với Rails, giải pháp của bạn nghe có vẻ thú vị. Để tạo lịch biểu và các lần xuất hiện liên quan, bạn có sử dụng gọi lại điều kiện trong mô hình Sự kiện không?

Trong trường hợp của tôi, người dùng có thể tạo sự kiện, định kỳ hàng tuần hay không. Vì vậy, tôi đã suy nghĩ về một trường boolean định kỳ trong mô hình sự kiện. Vì vậy, tôi đoán bạn sẽ có một callback đầu tiên để tạo ra lịch trình:

before_save :create_weekly_schedule, if: :recurring 

và cơ bản là một một giây để tạo ra các lần xuất hiện:

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 

Điều này âm thanh hợp lý với giải pháp của bạn? Thx

+1

Tôi thấy rằng bạn đang trả lời [câu trả lời của tôi] (http://stackoverflow.com/a/10266450/578288). Khi bạn trả lời một câu trả lời cụ thể, bạn nên đăng nhận xét về câu trả lời đó, nếu không tác giả sẽ không được thông báo. Ngoài ra, nếu câu hỏi của bạn dài (như câu hỏi này), đừng đặt câu hỏi đó vào câu trả lời khác về câu hỏi gốc; tạo câu hỏi mới trên trang web liên kết đến câu trả lời bạn đang trả lời. –

+0

Đối với những gì tôi nghĩ về giải pháp của bạn, tôi thấy một vấn đề với nó. Bạn nói 'schedule.each' - nhưng nếu lịch biểu đơn giản là" lặp lại mỗi tuần ", thì vòng lặp' each' sẽ kéo dài mãi mãi, bởi vì lịch biểu không chỉ định ngày kết thúc. Đó là lý do tại sao tôi không tạo ra tất cả các sự kiện trước thời hạn - có một số lượng vô hạn của chúng. –

Các vấn đề liên quan