Ruby on Rails 3 Multiple Associations

84 views Asked by At

I have the following associations in place:

class User < ActiveRecord::Base
  has_many :shopping_requests
  has_many :recommendations, :through => :shopping_requests
end


class ShoppingRequest < ActiveRecord::Base
  belongs_to :user
  has_many :recommendations
end


class Recommendation < ActiveRecord::Base
  belongs_to :shopping_request
  has_one :user, :through => :shopping_requests
end

Now I need to add a Compliment class. A user can compliment another user (so I have a user_from_id and a user_to_id). A compliment can be given for either a shopping request and/or a recommendation; and there's no limit (a user can be given several compliments by the same user or other users for any number of shopping requests as well as for recommendations).

I do know to make the Compliment polymorphic but not sure what is the best way to set it up in relation to users/shopping requests/recommendations.

I want to be able to run queries like this:

  • user_to_id.compliments (to get all the compliments for the user);
  • user_to_id.shopping_request.compliments (to get all that user's compliments for a particular shopping request;
  • user_to_id.recommendation.compliments (to get all that user's compliments for a particular recommendation; for this particular query, running user_to_id.shopping_request.recommendation.compliments is fine too);
  • user_from_id.compliments (to get all the compliments that a user gave another);
  • user_from_id.shopping_request.compliments (to get all the compliments given by this user for a particular shopping_request), etc....

So what is the best way to set up the association for the Compliment class?

1

There are 1 answers

0
Lanny Bose On BEST ANSWER

Here's my first swing. Your already-written code works, and I haven't reproduced it here.

class User < ActiveRecord::Base
  ...
  has_many :outgoing_compliments, class_name: "Compliment", foreign_key: "from_id"
  has_many :incoming_request_compliments, through: :shopping_requests, source: compliments
  has_many :incoming_recommendation_compliments, through: :recommendations, source: compliments
  ...
end


class ShoppingRequest < ActiveRecord::Base
  ...
  has_many compliments, as: :compliment able
  ...
end


class Recommendation < ActiveRecord::Base
  ...
  has_many compliments, as: :complimentable
  ...
end

class Compliment < ActiveRecord::Base
  belongs_to :complimentable, polymorphic: true
  #relies on two DB columns, complimentable_id and complimentable_type

  belongs_to :complimenter, class_name: "User", foreign_key: "from_id"
end

I made one change to your database as you've defined it. Compliment knows which Complimentable it belongs to, and since each Complimentable knows its User, saving the complemented-User is redundant. You could choose to add the lines...

class Compliment
  belongs_to :complimented, class_name: "User", foreign_key: "from_id"

class User
  has_many :incoming_compliments, class_name: "Compliment", foreign_key: "to_id"

...but I don't think I would.

Those are the associations you'll need to create. However, some of your desired method calls aren't specific enough. One example:

user_to_id.shopping_request.compliments (to get all that user's compliments for a particular shopping request;

Because what you've written is an instance method for User, we can assume the User is known. However, since a User can have many ShoppingRequests, it isn't possible, through what you've written, to hone in on one specific request to show Compliments for.