Ето един начин да го моделирате. Да кажем, че имаме модел „Engagement“, който има начална дата и час, крайна дата и час. Ангажиментът има много потребители чрез друга таблица за присъединяване, наречена „user_engagements“ (със съответния модел UserEngagement). Така че имаме
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
Сега имаме хубава проста схема. Ангажиментът основно моделира нещо, което се случва, а user_engagements моделира потребители, които са резервирани да направят това. Имаме предположение (не е записано в кода), че когато правят нещо, не са на разположение да правят нищо друго.
Следващата ни задача е да напишем метод, който връща наличните потребители в рамките на даден период от време, т.е. нов ангажимент. И така, ние правим ангажимент и искаме всички потребители, които нямат ангажимент, да преминават с нашия нов ангажимент. Мисля, че най-простият начин да направите това може да бъде да намерите всички потребители, които имат преминаващ ангажимент и след това да върнете всички потребители, които не са те. Ако ме разбираш. По-точен начин да се каже, че e2 пресича с e1 е, че e2 започва преди края на e1 И завършва след началото на e1.
Нека направим това метод на обект на ангажимент, тъй като той напълно зависи от данните на ангажимента.
#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
Имам чувството, че има по-ефективен начин да получа това с една заявка, но не мога да сложа пръст върху sql.