2

I'm trying to connect the values of two join tables that I have and show the results based on a conditional relationship...and i'm having some problems

I have a Users Model(:name, :password, :email), and Events model(:name, :etc) and Interests model (:name)

I created about 5 records in each model.

Then I created two join tables -> UsersInterests and EventsInterests; each not containing a primary key and only comprised of the user_id/interest_id and event_id/interest_id respectively.

Then I added to the model files the HABTM Relationship

users => has_and_belongs_to_many :interests
events => has_and_belongs_to_many :interests
interests => has_and_belongs_to_many :users
         has_and_belongs_to_many :events

Now I wanted to create a controller that finds only the events where the users interests correspond with the events interests

From working on this for a while I've figured that I need something in the area of

@Events = Event.User.find([condition])
[condition] = where users.interest == event.interest

またはそのようなもの...私はちょっと迷っています..検索条件をどのように述べていますか?...SQLで内部結合を行う方法は知っていますが、これを行うエレガントなRailsの方法を探しています. .. ヒントはありますか?

4

1 に答える 1

7

これを行う洗練された Ruby の方法は、名前付きスコープを使用することです。ただし、 has_many :through リレーションシップの代わりに has_and_belongs_to_many リレーションシップを使用することにしたため、生の SQL で結合を定義する必要がありますが、これはあまり洗練されていません。また、Rails が SQL 生成を処理する方法のために、1 人のユーザーで使用するためのスコープと、多数のユーザーで使用するための 2 つ目の名前付きスコープを作成する必要があります。

Class Event < ActiveRecord::Base
  ...

  #find events that share an interest with a single user
  named_scope :shares_interest_with_user, lambda {|user|
    { :joins => "LEFT JOIN events_interests ei ON ei.event_id = events.id " +
         "LEFT JOIN users_intersets ui ON ui.interest_id = ei.interest_id",
      :conditions => ["ui.user_id = ?", user], :group_by => "events.id"
    }

  #find events that share an interest with a list of users
  named_scope :shares_interest_with_users, lambda {|users|
    { :joins => "LEFT JOIN events_interests ei ON ei.event_id = events.id " +
         "LEFT JOIN users_intersets ui ON ui.interest_id = ei.interest_id",
      :conditions => ["ui.user_id IN ?", users], :group_by => "events.id"
    }    
  }

  #find events that share an interest with any user
  named_scope :shares_interest_with_any_user, lambda {
    { :joins => "LEFT JOIN events_interests ei ON ei.event_id = events.id " +
         "JOIN users_intersets ui ON ui.interest_id = ei.interest_id",
      :conditions => "ui.user_id IS NOT NULL", :group_by => "events.id"
    }    
  }

end

これで、ユーザーが関心を持つ可能性のあるすべてのイベントを取得できます。

@events = Event.shares_interest_with_user(@user)

または、ユーザーのリストが関心を持つ可能性のあるすべてのイベントを取得するには、次のようにします。

@events = Event.shares_interest_with_users(@users)

しかし、私が警告したように、それは本当にエレガントではありません。

HABTM 関係の代わりに適切な結合モデルを使用して関係を has_many に再定義すると、結合を大幅に簡素化できます。あなたのケースでは、これが機能するためにプラグインを介してネストされた has manyが必要になります。注: 対応する has_many/belongs_to ステートメントを他のすべてのモデルに追加する必要があります。結合モデルでさえ。

Class Event < ActiveRecord::Base
  has_many :event_interests
  has_many :interests, :through => :event_interests
  has_many :user_interests, :through => :interests
  has_many :users, :through => :user_interests
  ...   

  #find events that share an interest with a list of users
  named_scope :shares_interest_with_users, lambda {|user|
    { :joins => :user_interests, :group_by => "events.id",
      :conditions => {:user_interests => {:user_id => user}}
    }    
  }

  #find events that share an interest with any user
  named_scope :shares_interest_with_any_user, lambda {
    { :joins => :user_interests, :group_by => "events.id",
      :conditions => "user_interests.user_id IS NOT NULL"
    }

end

これで、次のようになります。

@user = User.first; @users = User.find(1,2,3)

# @events = all events a single user would be interested in
@events = Event.shares_interest_with_users(@user)

# @events = all events any of the listed users would be interested in.
@events = Event.shares_interest_with_users(@user)

名前付きスコープを定義して、まだ発生していないイベントを選択し、2 つを連鎖させることもできます。

named_scope :future_events, lambda {
   { :conditions => ["start_time > ?", Time.now]}
}

Events.future_events #=> Events that haven't started yet.

# Events that a user would be interested in but only choose those 
# that haven't started yet.
Events.future_events.shares_interest_with_user(@user) 
于 2010-02-01T04:45:30.677 に答える