在我看来,您不需要在这里使用多态关系。你应该能够做到这一点:
class User < ActiveRecord::Base
has_many :participations, class_name: 'Participant'
has_many :conversations, through: :participations
has_many :conversation_items, through: :conversations
end
class Participant < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
end
class Conversation < ActiveRecord::Base
has_many :participants
has_many :conversation_items
end
class ConversationItem < ActiveRecord::Base
belongs_to :conversation
belongs_to :sender, class_name: 'User', foreign_key: 'sender_id'
end
对我来说,这作为一个领域模型更有意义,其中一个对话有很多参与者。您可以将发件人附加到对话项目,并且对话中的每个参与者(不是发件人)都是隐含的收件人。对话有发送者和接收者是没有意义的,因为对话是双向或多向对话。
多态关系在这里不起作用的原因是多态关系适用于表可以引用不同类型项目的情况。例如,假设您的 ConversationItem 有很多附件,并且附件也可以应用于帖子。你会有类似的东西:
class ConversationItem < ActiveRecord::Base
#...
has_many :attachments, as: :attachable
#...
end
class Post < ActiveRecord::Base
has_many :attachments, as: :attachable
end
class Attachment < ActiveRecord::Base
belongs_to :attachable, polymorphic: true
end
附件将具有字段attachable_id
和attachable_type
. 当您从对话项目中查询附件时,您将有一个类似的查询SELECT * FROM attachments where attachable_id = <id of the conversation item> and attachable_type = 'ConversationItem'
。因此,多态关联使您能够将模型附加到许多不同类型的其他模型。
在这种情况下,您可以看到拥有多态关系是如何没有意义的,除非您将拥有许多不同类型的模型来发送和接收对话项目,因为在您的 conversation_items 表中,您将拥有sender_type
, sender_id
, recipient_type
, recipient_id
。
您的模型遇到的另一个问题是您尝试使用不同的参数两次定义相同的关系。当您调用 时has_many :foo
,Rails 会生成一堆不同的方法,例如foos
、foos=
等等。如果您再次调用它,您只是使用新参数重新定义这些方法。
跟进
您不一定需要有 Participant 模型。拥有它使您能够让多个用户参与,并且可以避免为同一模型创建多个关系。您可以通过关系实现相同的目的has_and_belongs_to_many
,但是如果需要,您将失去将额外属性附加到连接表的能力。
例如,您希望允许人们退出对话而不删除他们曾经是参与者的信息,使用加入模型,您可以添加一个布尔字段调用active
,设置为false
某人退出的时间。使用 HABTM,您无法做到这一点。让参与者离开需要从连接表中完全删除该对。我通常更喜欢加入模型,比如这里的参与模型,因为你永远不知道你的模式会如何演变。
也就是说,这里是 HABTM 的一个例子:
class User < ActiveRecord::Base
has_and_belongs_to_many :conversations
end
class Conversation < ActiveRecord::Base
has_and_belongs_to_many :users
end
在这里,您没有连接模型,但您需要创建一个连接表才能使其工作。一个迁移的例子是:
create_table :conversations_users, :id => false do |t|
t.integer :conversation_id
t.integer :user_id
end
add_index :conversations_users, [:conversation_id, :user_id], unique: true
连接表的组成部分的名称按字母顺序排列,即conversations_users
不是users_conversations
。
进一步跟进
如果您使用 Participant 表,外键将为 onparticipants
和conversation_items
。
participants
user_id
conversation_id
conversation_items
conversation_id
sender_id