模型关系
您的Favorite 模型如下所示:
class Favorite < ActiveRecord::Base
belongs_to :favoritable, polymorphic: true
belongs_to :user, inverse_of: :favorites
end
然后,您的User 模型将如下所示:
class User < ActiveRecord::Base
has_many :favorites, inverse_of: :user
end
那么,可以收藏的模型应该是这样的:
class Category < ActiveRecord::Base
has_many :favorites, as: :favoritable
end
是的,您的数据库中需要一个favorites 表。
收藏项目
因此,这应该允许您执行以下操作:
@user.favorites << Favorite.new(favoritabe: Category.find(1)) # add favorite for user
请记住,您需要将Favorite 的实例添加到@user.favorites,而不是favoritable 模型的实例。 favoritable 模型是Favorite 实例上的一个属性。
但是,实际上,在 Rails 中执行此操作的首选方式如下:
@user.favorites.build(favoritable: Category.find(1))
寻找某一类的最爱
如果您只想查找某种类型的收藏夹,您可以执行以下操作:
@user.favorites.where(favoritable_type: 'Category') # get favorited categories for user
Favorite.where(favoritable_type: 'Category') # get all favorited categories
如果您要经常这样做,我认为将作用域添加到多态模型是非常干净的:
class Favorite < ActiveRecord::Base
scope :categories, -> { where(favoritable_type: 'Category') }
end
这允许你做:
@user.favorites.categories
从上面得到的结果与@user.favorites.where(favoritable_type: 'Category') 相同。
允许用户只收藏一个项目一次
我猜您可能还希望允许用户只能收藏一个项目一次,这样您就不会在执行@user.favorites.categories 之类的操作时出现重复的类别。以下是在 Favorite 模型上设置的方法:
class Favorite < ActiveRecord::Base
belongs_to :favoritable, polymorphic: true
belongs_to :user, inverse_of: :favorites
validates :user_id, uniqueness: {
scope: [:favoritable_id, :favoritable_type],
message: 'can only favorite an item once'
}
end
这使得收藏夹必须具有user_id、favoritable_id 和favoritable_type 的唯一组合。由于favoritable_id 和favoritable_type 组合在一起得到收藏项,这相当于指定所有收藏项必须具有user_id 和favoritable 的唯一组合。或者,用简单的英语来说,“一个用户只能收藏一次”。
向数据库添加索引
出于性能原因,当您具有多态关系时,您需要在_id 和_type 列上建立数据库索引。如果您使用带有多态选项的 Rails 生成器,我认为它会为您执行此操作。否则,您将不得不自己做。
如果您不确定,请查看您的 db/schema.rb 文件。如果您在 favorites 表的架构之后有以下内容,那么您就大功告成了:
add_index :favorites, :favoritable_id
add_index :favorites, :favoritable_type
否则,将这些行放入迁移中并运行那个坏男孩。
当您使用它时,您应该确保您的所有外键也有索引。在此示例中,这将是 favorites 表上的 user_id 列。同样,如果您不确定,请检查您的架构文件。
关于数据库索引的最后一件事:如果您要添加上一节所述的唯一性约束,您应该向数据库添加唯一性索引。你可以这样做:
add_index :favorites, [:favoritable_id, :favoritable_type], unique: true
这将在数据库级别强制执行唯一性约束,如果您有多个应用服务器都使用单个数据库,这是必要的,并且通常是正确的做事方式。