We are trying to add multiple favoritable objects, where a user can favorite many different objects, but are not sure how to make it work.
Here is the Favorite model:
class Favorite < ActiveRecord::Base
# belongs_to :imageable, polymorphic: true
belongs_to :user
belongs_to :category
belongs_to :business
belongs_to :ad_channel
belongs_to :location
belongs_to :offer
end
The user model:
class User < ActiveRecord::Base
has_many :favorites, as: :favoritable
end
And one example model of something that can be favorited:
class Category < ActiveRecord::Base
has_many :sub_categories
has_many :ad_channels
has_many :offers
belongs_to :favoritable, polymorphic: true
end
I'm not sure if this is set up properly so that would be the first thing we need some feedback on.
Secondly how do we "favorite" something for a user?
This is what we've tried so far unsuccessfully:
@user.favorites << Category.find(1)
EDIT: Also will this need a favorites database table to record things? This is a pretty new concept for us.
Model Relationships
Your
Favorite
model looks like this:Then, your
User
model will look like this:Then, the models that can be favorited should look like this:
Yes, you will need a
favorites
table in your database.Favoriting Items
So, this should allow you to do stuff like:
Just keep in mind that you need to add instances of
Favorite
to@user.favorites
, not instances offavoritable
models. Thefavoritable
model is an attribute on the instance ofFavorite
.But, really, the preferred way to do this in Rails is like so:
Finding Favorites of a Certain Kind
If you wanted to find only favorites of a certain type, you could do something like:
If you're going to do this often, I think adding scopes to a polymorphic model is pretty clean:
This allows you to do:
Which is gets you the same result as
@user.favorites.where(favoritable_type: 'Category')
from above.Allowing Users to Favorite an Item Only Once
I'm guessing that you might also want to allow users to only be able to favorite an item once, so that you don't get, for example, duplicate categories when you do something like,
@user.favorites.categories
. Here's how you would set that up on yourFavorite
model:This makes it so that a favorite must have a unique combination of
user_id
,favoritable_id
, andfavoritable_type
. Sincefavoritable_id
andfavoritable_type
are combined to get the favoritable item, this is equivalent to specifying that all favorites must have a unique combination ofuser_id
andfavoritable
. Or, in plain English, "a user can only favorite something once".Adding Indexes to the Database
For performance reasons, when you have polymorphic relationships, you want database indexes on the
_id
and_type
columns. If you use the Rails generator with the polymorphic option, I think it will do this for you. Otherwise, you'll have to do it yourself.If you're not sure, take a look your
db/schema.rb
file. If you have the following after the schema for yourfavorites
table, then you're all set:Otherwise, put those lines in a migration and run that bad boy.
While you're at it, you should make sure that all of your foreign keys also have indexes. In this example, that would be be the
user_id
column on thefavorites
table. Again, if you're not sure, check your schema file.And one last thing about database indexes: if you are going to add the uniqueness constraint as outlined in the section above, you should add a unique index to your database. You would do that like this:
This will enforce the uniqueness constraint at the database level, which is necessary if you have multiple app servers all using a single database, and generally just the right way to do things.