Oto jeden sposób na modelowanie tego. Powiedzmy, że mamy model „Zaręczyny”, który ma datę rozpoczęcia, godzinę zakończenia i nazwę. Zaangażowanie ma wielu użytkowników za pośrednictwem innej tabeli sprzężenia o nazwie „user_engagements” (z odpowiednim modelem UserEngagement). Więc mamy
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
Teraz mamy ładny, prosty schemat. Zaangażowanie zasadniczo modeluje coś, co się dzieje, a user_engagements modeluje użytkowników, którzy mają to zrobić. Mamy założenie (nie zapisane w kodzie), że kiedy coś robią, nie są w stanie zrobić nic innego.
Naszym kolejnym zadaniem jest napisanie metody, która zwraca użytkowników dostępnych w danym przedziale czasowym, czyli nowe zaangażowanie. Tak więc angażujemy się i chcemy wszystkich użytkowników, którzy nie mają zaangażowania, które przecina się z naszym nowym zaangażowaniem. Myślę, że najprostszym sposobem na zrobienie tego może być znalezienie wszystkich użytkowników, którzy są zaangażowani, a następnie zwrócenie wszystkich użytkowników, którzy nimi nie są. Jeśli wiesz co mam na myśli. Bardziej precyzyjnym sposobem powiedzenia, że e2 krzyżuje się z e1 jest to, że e2 zaczyna się przed końcem e1 ORAZ kończy się po rozpoczęciu e1.
Zróbmy z tego metodę obiektu zaangażowania, ponieważ jest to całkowicie zależne od danych dotyczących zaangażowania.
#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
Czuję, że istnieje bardziej wydajny sposób na uzyskanie tego w jednym zapytaniu, ale nie mogę położyć palca na sql.