我正在rails 4上创建一个简单的聊天应用程序。创建了控制器,模型和视图,但功能仍然不完整。我的数据库,对话和消息中有2个表。会话表包含两个字段,发件人ID和接收者ID。消息表包含3个字段,正文,用户ID和读取(默认为0表示不读取)。
型号:
class Conversation < ActiveRecord::Base
belongs_to :sender, :foreign_key => :sender_id, :class_name => "User"
belongs_to :reciever, :foreign_key => :reciever_id, :class_name => "User"
has_many :messages, :dependent => :destroy
validates_uniqueness_of :sender_id, :scope => :reciever_id
scope :involving, lambda { |user_id|
where("sender_id = ? OR reciever_id = ?", user_id, user_id)
}
scope :between, lambda { |sender_id, reciever_id|
where("(sender_id = ? AND reciever_id = ?) OR (sender_id = ? AND reciever_id = ?)", sender_id, reciever_id, reciever_id, sender_id)
}
def other_interlocutor(user_id)
if sender.id == user_id
return reciever.id
else
return sender.id
end
end
end
class Message < ActiveRecord::Base
belongs_to :conversation
belongs_to :user
validates_presence_of :conversation_id, :user_id, :body
end
我想要做的是创建一个实时的功能,即每当有人收到新消息时接收未读消息。我正在使用私人酒吧来创建用户之间的聊天。
我有一个包含此功能的用户模型:
def unread_messages_count
unread_messages = 0
# puts "Putting self conversations ! #{self.conversations.first}"
conversations = Conversation.involving(self.id)
conversations.each do |conversation|
unread_messages += conversation.messages.where(:read => 0, :user_id => conversation.other_interlocutor(self.id)).count
end
return unread_messages = unread_messages == 0 ? nil : unread_messages
end
我有一个页面列出了所有用户的对话,并且单击了一个对话,所有与该对话相关的消息也被列出。在同一页面上,我订阅了每个conversation_messages_path
,为每个会话创建单独的频道。每当发送消息时,都会呈现create.js.erb
文件,我将其发布到那些订阅的频道:
<% publish_to conversation_messages_path(@conversation.id) do %>
$("#conversations_link").text("<%= current_user.unread_messages_count %> Conversations");
$("#messages").append("<%= escape_javascript render(:partial => 'message', :locals => { :message => @message })%>");
<% end %>
$("#conversation_link")
是我想要显示未读消息的地方。
目前,未读消息计数返回错误计数,导航栏仅在conversation.sender_id
向接收方发送消息时更新。
我的未读邮件计数器未返回正确数量的未读邮件。我不知道如何解决它。我的代码有什么问题? 感谢。
答案 0 :(得分:3)
我认为你的域建模真的没有了。
谈话的整个想法是有关各方轮流担任发件人和收件人。您的模拟是独白。
独白是一个人发表的讲话,或者是片面的长篇大论 谈话让你想要从乏味中拔出你的头发。 希腊词根monologos翻译为“独自说话”和 这是一个独白:一个人在做所有的谈话。
您最终使用的域名模型应如下所示:
其实际链接到两个用户(或更多)的消息:sender
和recipient
。为了简单起见,我在这里坚持1:1的消息传递(对于群组聊天,消息可能属于许多收件人)。
class Message
belongs_to :recipient, class_name: 'User'
belongs_to :sender, class_name: 'User'
end
class User
has_many :sent_messages,
class_name: 'Message',
foreign_key: 'sender_id'
has_many :messages, foreign_key: 'recipient_id'
end
请注意,当无法从关联名称派生时,我们需要告诉Rails类和外键。
您可能需要考虑使用enum来表示消息的状态,而不是使用布尔read
字段。
枚举基本上是一个整数列,映射到符号列表。
class Message
enum :status, [:unread, :read]
belongs_to :recipient, class_name: 'User'
belongs_to :sender, class_name: 'User'
belongs_to :conversation
end
Enums为您提供以下范围:
Message.unread
Message.read
以及条件如:
message.unread?
message.read?
如果您想添加更多状态,例如:archieved
或:trashed
,它会非常直接。
有了这个,你不需要unread_messages_count
怪物。因为你将记录从数据库中拉出来计算相关记录,所以会占用大量内存。
current_user.messages.unread.size
我们还应该正确定义用户和对话之间的关系:
class Conversation
has_many :messages
has_and_belongs_to_many :users
end
class Users
# ..
has_and_belongs_to_many :conversations
end
此has_and_belongs_to_many关系会在users_conversations
联接表中存储用户和转化。您可以使用以下生成器来创建连接表迁移:
rails generate migration users_conversations
在您的观看中使用session[:user_id]
是一个非常糟糕的代码味道。您将整个应用程序中的身份验证逻辑紧密耦合。
而是创建一个帮手:
class SessionsHelper
def current_user
@current_user ||= User.find(session[:user_id])
end
def user_signed_in?
!current_user.nil?
end
end
应用程序的其他部分不应该知道您将当前用户存储在session[:user_id]
只是存在current_user
。