これをモデル化する1つの方法があります。開始日時、終了日時、名前を持つモデル「エンゲージメント」があるとします。エンゲージメントには、「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
1つのクエリでこれを取得するためのより効率的な方法があるように感じますが、SQLに完全に指を置くことはできません。