Rails-变形收藏夹(用户可以收藏不同的模型) [英] Rails - Polymorphic Favorites (user can favorite different models)

查看:103
本文介绍了Rails-变形收藏夹(用户可以收藏不同的模型)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在尝试添加多个收藏夹对象,用户可以在其中收藏许多不同的对象,但不确定如何使它起作用.

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.

这是收藏夹"模型:

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

用户模型:

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)

另外,这是否需要收藏夹数据库表来记录内容?对我们来说,这是一个非常新的概念.

Also will this need a favorites database table to record things? This is a pretty new concept for us.

推荐答案

模型关系

您的Favorite模型如下所示:

class Favorite < ActiveRecord::Base
  belongs_to :favoritable, polymorphic: true
  belongs_to :user, inverse_of: :favorites
end

然后,您的User模型将如下所示:

Then, your User model will look like this:

class User < ActiveRecord::Base
  has_many :favorites, inverse_of: :user
end

然后,可以收藏的模型应如下所示:

Then, the models that can be favorited should look like this:

class Category < ActiveRecord::Base
  has_many :favorites, as: :favoritable
end

是的,您的数据库中将需要一个favorites表.

Yes, you will need a favorites table in your database.

因此,这应该允许您执行以下操作:

So, this should allow you to do stuff like:

@user.favorites << Favorite.new(favoritabe: Category.find(1))  # add favorite for user

请记住,您需要将Favorite的实例添加到@user.favorites,而不是favoritable模型的实例. favoritable模型是Favorite实例的属性.

Just keep in mind that you need to add instances of Favorite to @user.favorites, not instances of favoritable models. The favoritable model is an attribute on the instance of Favorite.

但是,实际上,在Rails中执行此操作的首选方式是这样的:

But, really, the preferred way to do this in Rails is like so:

@user.favorites.build(favoritable: Category.find(1))

查找某种收藏夹

如果您只想查找某种类型的收藏夹,则可以执行以下操作:

Finding Favorites of a Certain Kind

If you wanted to find only favorites of a certain type, you could do something like:

@user.favorites.where(favoritable_type: 'Category')  # get favorited categories for user
Favorite.where(favoritable_type: 'Category')         # get all favorited categories

如果您要经常执行此操作,我认为将范围添加到多态模型中非常干净:

If you're going to do this often, I think adding scopes to a polymorphic model is pretty clean:

class Favorite < ActiveRecord::Base
  scope :categories, -> { where(favoritable_type: 'Category') }
end

这允许您执行以下操作:

This allows you to do:

@user.favorites.categories

从上方获得与@user.favorites.where(favoritable_type: 'Category')相同的结果.

Which is gets you the same result as @user.favorites.where(favoritable_type: 'Category') from above.

我猜测您可能还希望只允许用户收藏一个项目一次,这样,当您执行@user.favorites.categories之类的操作时就不会出现重复的类别.在您的Favorite模型上进行设置的方法如下:

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 your Favorite model:

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_idfavoritable_idfavoritable_type的唯一组合.由于将favoritable_idfavoritable_type组合在一起以获得最喜欢的项目,所以这等效于指定所有收藏夹必须具有user_idfavoritable的唯一组合.或者用简单的英语说,用户只能收藏一次东西."

This makes it so that a favorite must have a unique combination of user_id, favoritable_id, and favoritable_type. Since favoritable_id and favoritable_type are combined to get the favoritable item, this is equivalent to specifying that all favorites must have a unique combination of user_id and favoritable. Or, in plain English, "a user can only favorite something once".

出于性能原因,当您具有多态关系时,需要在_id_type列上使用数据库索引.如果您将Rails生成器与多态选项一起使用,我认为它将为您完成此任务.否则,您必须自己做.

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.

如果不确定,请查看您的db/schema.rb文件.如果favorites表的模式后面有以下内容,则说明一切就绪:

If you're not sure, take a look your db/schema.rb file. If you have the following after the schema for your favorites table, then you're all set:

add_index :favorites, :favoritable_id
add_index :favorites, :favoritable_type

否则,将这些行放入迁移中并运行那个坏孩子.

Otherwise, put those lines in a migration and run that bad boy.

使用它时,应确保所有外键都具有索引.在此示例中,这将是favorites表上的user_id列.同样,如果不确定,请检查您的架构文件.

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 the favorites 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:

add_index :favorites, [:favoritable_id, :favoritable_type], unique: true

这将在数据库级别强制执行唯一性约束,如果您有多个应用程序服务器都使用一个数据库,则这是必需的,并且通常是正确的处理方式.

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.

这篇关于Rails-变形收藏夹(用户可以收藏不同的模型)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆