2016-02-23 92 views
0

我正在使用我的rails应用程序中的消息传递系统。我已经在2个用户(发件人和收件人)之间发送邮件正常工作。这种设置很好,但我怎样才能为每个房间进行一次新的对话,以便唯一性检查将仅在用户和房间之间进行或反之亦然?每个用户只能从房间显示页面发送消息到房间。所以room_id可以在那里找到。单个用户可能有许多列表,这使得它对我来说很复杂。所以我很困惑在下面的代码中做了什么改变来实现这个目标?或者我必须为模型做出不同的设计方法? 我有一个userlistingconversationmessage模型在rails中的私人消息传递系统

conversation.rb

class Conversation < ActiveRecord::Base 
    belongs_to :sender, foreign_key: :sender_id, class_name: 'User' 
    belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User' 

    has_many :messages, dependent: :destroy 

    validates_uniqueness_of :sender_id, scope: :recipient_id 

    scope :involving, -> (user) do 
    where("conversations.sender_id = ? OR conversations.recipient_id = ?", user.id, user.id) 
    end 

    scope :between, -> (sender_id, recipient_id) do 
    where("(conversations.sender_id = ? AND conversations.recipient_id = ?) OR (conversations.sender_id = ? AND conversations.recipient_id = ?)", 
      sender_id, recipient_id, recipient_id, sender_id) 
    end 
end 

Message.rb

class Message < ActiveRecord::Base 
     belongs_to :conversation 
     belongs_to :user 

     validates_presence_of :content, :conversation_id, :user_id 

     def message_time 
     created_at.strftime("%v") 
     end 
    end 


conversations_controller.rb 

class ConversationsController < ApplicationController 
    before_action :authenticate_user! 

    def index 
      @conversations = Conversation.involving(current_user) 
    end 

    def create 
    if Conversation.between(params[:sender_id], params[:recipient_id]).present? 
     @conversation = Conversation.between(params[:sender_id], params[:recipient_id]).first 
    else 
     @conversation = Conversation.create(conversation_params) 
    end 

    redirect_to conversation_messages_path(@conversation) 
    end 

    private 

    def conversation_params 
    params.permit(:sender_id, :recipient_id) 
    end 
end 

messages_controller.rb

class MessagesController < ApplicationController 
    before_action :authenticate_user! 
    before_action :set_conversation 

    def index 
    if current_user == @conversation.sender || current_user == @conversation.recipient 
     @other = current_user == @conversation.sender ? @conversation.recipient : @conversation.sender 
     @messages = @conversation.messages.order("created_at DESC") 
    else 
     redirect_to conversations_path, alert: "You don't have permission to view this." 
    end 
    end 

    def create 
    @message = @conversation.messages.new(message_params) 
    @messages = @conversation.messages.order("created_at DESC") 

    if @message.save 
     redirect_to conversation_messages_path(@conversation) 
    end 
    end 

    private 

    def set_conversation 
    @conversation = Conversation.find(params[:conversation_id]) 
    end 

    def message_params 
    params.require(:message).permit(:content, :user_id) 
    end 
end 

回答

2

你的关系是关闭的。发送者和接收者固定的谈话并不好 - 事实上那只是一个独白!

相反,我们需要一个真正的多对多关系。这意味着我们需要第三个表来存储用户和converstations

之间的联系

所以让我们通过生成模式启动:

rails g model UserConversation user:belongs_to conversation:belongs_to 

,这将产生一个连接表将连接用户的模型和迁移和谈话。我们现在也应该关注唯一性要求。打开迁移:

class CreateUserConversations < ActiveRecord::Migration 
    def change 
    create_table :user_conversations do |t| 
     t.belongs_to :user, index: true, foreign_key: true 
     t.belongs_to :conversation, index: true, foreign_key: true 

     t.timestamps null: false 
    end 

    # Add this constraint 
    add_index :user_conversations, [:user_id, :conversation_id], unique: true 
    end 
end 

该约束可确保数据库级别的唯一性并防止竞争条件。我们还希望在软件级别进行验证。

class UserConversation < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :conversation 

    validates_presence_of :user_id, scope: :conversation_id 
end 

现在我们建立在用户和对话的关系,使他们通过加入模型去:

class User < ActiveRecord::Base 
    has_many :user_conversations 
    has_many :conversations, through: user_conversations 

    def has_joined?(conversation) 
    conversations.where(id: conversation).exist? 
    end 
end 

class Conversation < ActiveRecord::Base 
    has_many :user_conversations 
    has_many :messages 
    has_many :users, through: user_conversations 

    def includes_user?(user) 
    users.where(id: user).exist? 
    end 
end 

这让我们做@user.conversations@conversation.users。我们不需要哈克范围。

这是你怎么可以一个用户可能添加到对话中对飞一个例子:你还是有相当的路要走,可能会需要几个不同的控制器正确

class MessagesController < ApplicationController 

    # ... 

    def create 
    unless current_user.has_joined?(conversation) 
     # @todo handle case where this fails 
     @conversation.users << current_user 
    end 

    @message = @conversation.messages.new(message_params) do |m| 
     # get the current user from the session or a token 
     # using params is an open invitation for hacking 
     m.user = current_user 
    end 

    if @message.save 
     redirect_to conversation_messages_path(@conversation) 
    else 
     render :new 
    end 
    end 

    # ... 
end 

但需要注意的代表消息在不同的上下文中:

/messages/:id => MessagesController 
/users/:user_id/messages => Users::MessagesController 
/conversations/:id/messages => Conversations::MessagesController 
+1

这只是如何解决您的应用程序中的一些问题的开始。这是一个非常常见的域名,因此您可能想要使用消息传递系统来扫描github以获取开源应用程序,并检查他们是如何处理涉及的问题的。 – max

+0

非常感谢mate ..这有助于...是不是像提到多态为真? – Abhilash

+1

不,多态关系是你有关系的地方,关联记录的类型是动态的。就像如果你有一个可以属于“文章”或“幻灯片”的图片一样。这是一个通过连接模型的多对多关系。 http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association – max