25

Sto cercando il modo migliore per modellare eventi ricorrenti. Sto usando fullcalendar per visualizzare gli eventi. Ma credo che gli eventi ricorrenti siano gestiti meglio sul back-end dei binari.Eventi ricorrenti nel calendario - Rails

Ho già esaminato altre domande e codice di esempio esistente ma non ho trovato nulla che si adatti.

Dovrebbe funzionare come il calendario di Google. Quindi dovrebbe essere possibile per cancellare/modificare singoli eventi della serie di eventi ricorrenti. Ma salvare tutti gli eventi della serie di eventi nel database sembra inefficiente. Inoltre dovrebbe essere possibile creare singoli eventi senza alcuna ricorrenza.

Quale sarebbe una buona architettura di modello?

Il mio modello di eventi in questo momento sembra che (senza attributi aggiuntivi):

# 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 

risposta

37

Ecco come modellerei questo. Non ho usato molto Google Calendar, quindi sto basando la funzionalità sugli eventi ricorrenti di iCal.

Tutti i modelli devono avere le solite proprietà id, created_at, updated_at. Sono elencate le proprietà personalizzate. Se la proprietà è un altro modello, verrà implementata un'associazione come has_one o belongs_to.

  • RecurrencePeriod
    • Event base_event # has_one :base_event, :class_name'Event'
    • Time data_finale # può essere pari a zero, se si ricorre sempre
    • WeeklyRecurrence ricorrenza # has_one :recurrence, :as=>:recurrence
    • Array[OccurrenceOverride] override # has_many :overrides, :class_name=>'OccurrenceOverride'

Le RecurrencePeriod decorre dalla data in cui la sua base_event inizia. Inoltre, suppongo che un employee_id di Event si riferisca al dipendente che ha creato quell'evento. Un RecurrencePeriod apparterrà anche al dipendente che ha creato l'evento base_event.

Il modello dipende dalla flessibilità con cui si desidera essere in grado di specificare le ricorrenze. Avete intenzione di sostenere "martedì e giovedì ogni due settimane dalle 10 alle 11 e dalle 14 alle 15" o semplicemente "si ripete settimanalmente"? Ecco un modello che supporta solo "ripetizioni settimanali", "si ripete ogni due settimane", ecc .; puoi espanderlo se necessario.

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

Io uso polymorphic associations qui, perché penso che potrebbe essere utile se si desidera più di un tipo di ricorrenza, tale sia WeeklyRecurrence e DailyRecurrence. Ma non sono sicuro che siano il modo corretto di modellarlo, quindi se non risultano essere, usa semplicemente has_one :weekly_recurrence e belongs_to :recurrence_period.

La libreria Ice cube sembra essere utile per calcolare le ricorrenze. Se lo WeeklyRecurrence non è abbastanza potente, potresti semplicemente voler archiviare un oggetto Ice cube Schedule in un modello, sostituendo WeeklyRecurrence. Per memorizzare un oggetto Schedule in un modello, salvarlo come attributo "pianificazione", inserire serialize :schedule nella definizione del modello e generare una colonna di testo "pianificazione" nel database.

OccurrenceOverride gestisce il caso di una singola istanza di un evento ricorrente in corso di modifica.

  • OccurrenceOverride
    • RecurrencePeriod recurrence_period_to_override # belongs_to :recurrence_period_to_override, :class_name=>'RecurrencePeriod'
    • Time original_start_time # identifica univocamente che recidiva entro che RecurrencePeriod per sostituire
    • Event replacement_event # has_one :replacement_event, :class_name=>'Event'; può essere pari a zero, se che la ricorrenza è stata eliminata, invece di modificare

Invece di memorizzare ogni occorrenza di un evento singolarmente, generare temporaneamente quando si ha bisogno di mostrare loro la vista. In RecurrencePeriod, creare un metodo generate_events_in_range(start_date, end_date) che generi Event s, non per salvare nel database, ma solo per passare alla vista in modo che possa mostrarli.

Quando un utente modifica una ricorrenza, dovrebbe avere la possibilità di modificare tutte le occorrenze, tutte le occorrenze future o solo quell'evento. Se modificano tutte le occorrenze, modifica l'evento base RecurrencePeriod. Se modificano tutte le occorrenze future, utilizzare un metodo da implementare su RecurrencePeriod diviso in due RecurrencePeriod s su entrambi i lati di una determinata data, quindi salvare le modifiche solo nel secondo periodo. Se modificano solo quell'evento, crea un valore OccurrenceOverride per il tempo in cui stanno eseguendo l'override e salva le modifiche al comando replace_event dell'override.

Quando un utente dice che un determinato evento dovrebbe ripresentarsi ogni due settimane per il prossimo futuro, è necessario creare un nuovo RecurrencePeriod con quell'evento come base_event e un nil end_date. La sua ricorrenza dovrebbe essere una nuova WeeklyRecurrence con weeks_between_recurrence = 2 e non dovrebbe avere OccurrenceOverride s.

+1

Eventuali problemi relativi all'esperienza di non aver persistito un problema anche se non è stato possibile accedervi tramite ID? (prestazioni, complessità, ecc.) – ted

+0

@ted Buona domanda. Non l'ho mai implementato, quindi temo di non sapere se la generazione di occorrenze in base alle esigenze causi problemi. Suppongo che potresti creare una cache per le occorrenze generate, se necessario. Un sistema di memorizzazione nella cache potrebbe utilizzare il fatto che un'occorrenza generata da salvare è concettualmente molto simile a un 'OccurrenceOverride', sebbene non identico. –

3

Solo un parere fuori della parte superiore della mia testa, forse comenters indicherà un problema non sto pensando di al momento:

Farei un modello RecurringEvent (o qualsiasi altra cosa si voglia chiamare) quello has_many :events.

Supponiamo che ogni evento venga creato da un dipendente (in base alle note), quindi RecurringEvent sarebbe anche belong_to :employee. È quindi possibile creare una relazione has_many :through in cui un dipendente ha molti eventi e ha molti eventi ricorrenti.

Il modello RecurringEvent può avere una data di inizio e un motivo e inizialmente poteva utilizzare questo modello per creare i singoli eventi occorsi. Quindi, in qualsiasi evento che fa parte delle serie ricorrenti, è possibile modificare o eliminare quell'occorrenza individuale, ma è anche possibile "rigenerare la serie", eliminando tutti gli eventi della serie (o tutti gli eventi futuri della serie) e ricostruendoli in base a un nuovo modello, quindi spostare l'incontro da "ogni martedì" a "ogni giovedì".

Un altro tipo di cosa interessante è che è possibile creare una lista di eventi ricorrenti a colpo d'occhio, che potrebbe darvi una visione chiara dei principali obblighi delle persone.

Come ho detto, la parte superiore della mia testa è così che mi avvicino, ma questo è solo un'idea e non ho costruito niente di simile in modo da non so se ci sono dei grossi grattacapi nell'approccio che sto suggerendo.

Buona fortuna, per favore pubblica ciò che fai!

5

Nel mio caso ho fatto qualcosa di simile:

# 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 

Da lì, ho usato ice_cube di gestire il calcolo ricorrenze ed i risultati nella tabella occorrenze memorizzato. Prima ho provato a lavorare senza il modello Occurrence, ma non importa quanto avanzato sia il motore delle regole, avrai sempre delle eccezioni, quindi archiviare le occorrenze nel loro modello ti dà flessibilità.

Avere un modello di occorrenza rende molto più facile visualizzare gli eventi su un calendario o con i filtri di ricerca della data in quanto è sufficiente interrogare per le occorrenze e quindi visualizzare i dati dell'evento correlato invece di raccogliere tutti gli eventi in una determinata data intervallo e quindi dover filtrare gli eventi in cui la pianificazione (s) non corrispondono.

Inoltre è possibile bandiera verificarsi di un evento come annullato o modificarlo (impostando l'attributo generato a false in modo che non venga ripulito durante la modifica di un programma ice_cube ... o qualunque sia la vostra esigenza aziendale è)

Di Certo, se hai eventi che si ripetono all'infinito, dovrai limitare la distanza in cui desideri generare tali eventi e utilizzare le attività di rake automatizzate per ripulire quelli vecchi e generare occorrenze per il prossimo anno.

Finora questo schema funziona abbastanza bene per me.

Inoltre, dai un'occhiata alla gemma recurring_select che è un input di forma ice_cube piuttosto carino.

+2

Bel post. Curioso sui tuoi pensieri con questo approccio: http://blog.plataformatec.com.br/2010/04/recurring-events Sembra simile al tuo. – Bruno

+0

Bruno, l'approccio da te menzionato è valido fino a quando non è necessario mantenere lo stato su queste ricorrenze o gestire le eccezioni (es .: rappresentazione dei concerti sospesa al giorno successivo in caso di pioggia) – Jim

+0

@Jim Hai una demo app o app di esempio con cui posso scherzare? Mi piace la soluzione – Frank004

-1

Sono abbastanza nuovo per Rails, la soluzione sembra interessante. Per creare la pianificazione e le occorrenze associate, si usano le callback condizionali nel modello Event?

Nel mio caso, gli utenti sarebbero in grado di creare eventi, ricorrenti o meno. Quindi stavo pensando a un campo booleano ricorrente nel modello di eventi. Quindi credo che si dovrebbe avere un primo callback per creare il programma:

before_save :create_weekly_schedule, if: :recurring 

e fondamentalmente un secondo per creare le occorrenze:

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 

Questo suona logico con la soluzione? Thx

+1

Vedo che stavi rispondendo a [la mia risposta] (http://stackoverflow.com/a/10266450/578288). Quando rispondi a una risposta specifica, devi pubblicare un commento su tale risposta, altrimenti l'autore non riceverà alcuna notifica. Inoltre, se la tua domanda è lunga (come questa), non metterla in un'altra risposta alla domanda originale; crea una nuova domanda sul sito che rimanda alla risposta a cui stai rispondendo. –

+0

Per quanto riguarda ciò che penso della tua soluzione, vedo un problema con esso. Dite 'schedule.each' - ma se la pianificazione è semplicemente" ripetizioni ogni settimana ", allora il ciclo' each' durerà per sempre, perché la pianificazione non specifica una data di fine. Questo è il motivo per cui non ho generato tutti gli eventi in anticipo: ne esiste un numero infinito. –