Voici une façon de le modéliser. Disons que nous avons un modèle "Engagement" qui a une date et une heure de début, une date et un nom de fin. Un engagement a de nombreux utilisateurs, via une autre table de jointure appelée "user_engagements" (avec le modèle UserEngagement correspondant). Nous avons donc
User
has_many :user_engagements
has_many :engagements, :through => :user_engagements
Engagement
#fields - starts_at, ends_at (both datetime)
has_many :user_engagements
has_many :users, :through => :user_engagements
UserEngagement
belongs_to :user
belongs_to :engagement
Nous avons maintenant un joli schéma simple. Un engagement modélise essentiellement quelque chose qui se passe, et user_engagements modélise les utilisateurs qui sont réservés pour faire cette chose. Nous partons du principe (non écrit dans le code) que lorsqu'ils font quelque chose, ils ne sont pas disponibles pour faire autre chose.
Notre tâche suivante consiste à écrire une méthode qui renvoie les utilisateurs disponibles dans un délai donné, c'est-à-dire un nouvel engagement. Donc, nous faisons un engagement et nous voulons que tous les utilisateurs qui n'ont pas d'engagement se croisent avec notre nouvel engagement. Je pense que la façon la plus simple de procéder pourrait être de trouver tous les utilisateurs qui ont un engagement croisé, puis de renvoyer tous les utilisateurs qui ne le sont pas. Si tu vois ce que je veux dire. Une façon plus précise de dire que e2 croise avec e1 est que e2 commence avant la fin de e1 ET se termine après le début de e1.
Faisons-en une méthode d'un objet d'engagement puisqu'il dépend totalement des données d'un engagement.
#in Engagement
def unavailable_user_ids
User.find(:all, :include => [:user_engagements], :select => "users.id", :conditions => ["user_engagements.starts_at < ? and user_engagements.ends_at > ?", self.ends_at, self.starts_at]).collect(&:id)
end
def available_users
User.find(:all, :conditions => ["id not in (?)", self.unavailable_user_ids])
end
J'ai l'impression qu'il existe un moyen plus efficace d'obtenir cela en une seule requête, mais je n'arrive pas à mettre le doigt sur le sql.