I'm trying to set up a messaging system similar to facebook, where you have a list of messages sorted by chain between two users (this is not important for several recipients at the moment, but maybe if I used a more intelligent design it could be easily implemented in the future. I don’t think it would be easy to add to what I have now.) I have something like work, but I can’t implement several functions. I am new to rails / web programming, so all the tips and tricks / solutions are very appreciated.
So, I have three suitable models: User, Conversation and Messages. The user has a lot of talk. There are a lot of messages in the conversation and belongs to the Users, and Message belongs to the conversation. So, the models look like this:
User: has corresponding fields ID: int, username: string
class User < ActiveRecord::Base has_many :conversations, :class_name => "Conversation", :finder_sql => proc { "SELECT * FROM conversations " + "WHERE conversations.user1_id = #{id} OR conversations.user2_id = #{id} " + "ORDER BY conversations.updated_at DESC" }
Conversation: has corresponding fields: ID: int, user1_id: int, user2_id: int, user1_deleted: boolean, user2_deleted: boolean, created_at: datetime, updated_at: datetime
class Conversation < ActiveRecord::Base has_many :messages belongs_to :participant_one, :class_name => "User", :foreign_key => :user1_id belongs_to :participant_two, :class_name => "User", :foreign_key => :user2_id private def self.between(user1, user2) c = Conversation.arel_table Conversation.where(c[:user1_id].eq(user1).and(c[:user2_id].eq(user2)).or(c[:user1_id].eq(user2).and(c[:user2_id].eq(user1))))
Message: has corresponding fields: id: int, convers_id: int, author_id: int, content: text, created_at: datetime, updated_at: datetime
class Message < ActiveRecord::Base belongs_to :conversation, :touch => true
I'm not sure if I need participants_participant_participant_participant, and I use
def conversation_partner(conversation) conversation.participant_one == current_user ? conversation.participant_two : conversation.participant_one end
in the dialog box so that in the views I can show another participant.
So this basically works. But one of the complications that I have is that I do not distinguish users very well in the conversation, the user can be in the user1 field or in the user2 field. Therefore, I need to constantly search for a user in one or another field, for example. in finder_sql has_many user’s declarations. In addition, when I create a new message, I first search to see if there is a conversation parameter, or if it is not, see if there is a dialogue between the two users, and if not, create a new session. (You can either send a message from the conversation index (for example, the answer), or current_user can view another user and click the “Send this message to the user” link. This mechanism looks like this and uses it. Between the method in the “Conversation” model:
class MessagesController < ApplicationController before_filter :get_user before_filter :find_or_create_conversation, :only => [:new, :create] def new @message = Message.new end def create @message = @conversation.messages.build(params[:message]) @message.author_id = current_user.id if @message.save redirect_to user_conversation_path(current_user, @conversation), :notice => "Message sent!" else redirect_to @conversation end end private def get_user @user = User.find(params[:user_id]) end def find_or_create_conversation if params[:conversation_id] @conversation = Conversation.find(params[:conversation_id]) else @conversation = Conversation.between(@user.id, current_user.id).first or @conversation = Conversation.create!(:user1_id => current_user.id, :user2_id => @user.id) end end
(my routes look like this :)
resources :users do resources :conversations, :only => [:index, :create, :show, :destroy] do resources :messages, :only => [:new, :create] end resources :messages, :only => [:new] end
So, I am having problems setting the user1_deleted or user2_deleted flags. (and similarly, if / when I implement the read / update flag). The problem is that since the same user can have a lot of conversations, but he can be user1 or user2, it becomes difficult to find him. I thought I could do something like this in the Conversation model:
def self.active(user) Conversation.where(which_user?(user) + "_deleted = ?", false) end def self.which_user?(user) :user1_id == user ? 'user1' : 'user2' end
But then you cannot start it with the whole conversation if you do not iterate through each of the user's conversations one by one, because sometimes he is user1, and sometimes he is user2. Should I rip off this whole approach and try a new design? If so, can anyone find a possible approach that will be more elegant / work better / actually work and still meet the same needs?
This is a rather long question, so I appreciate anyone who wants to get through all this with me. Thanks.