1
votes

i got a simple messaging system running between two users. the user who is creating a conversation gets a record i can access via User.find(sender_id).conversation.last, the recipient however gets a => nil if i try to access User.find(recipient_id).conversation.last (ofc i replaced recipient_id and sender_id with the actual id).

However, both user get the messages associated with the conversation and can chat. I am using foreign keys according to the tutorial of Dana Mulder on Medium. i think this is weird because i expect that both users ( the reciever and the sender ) should be able to access the record for the conversation they are into. is there a way to achieve that?

my models:

User.rb

has_many :conversations, :foreign_key => :sender_id, dependent: :destroy
has_many :messages, through: :conversations

Conversation.rb

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

Message.rb

belongs_to :conversation
belongs_to :user

the conversations controller:
conversations_controller.rb

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

do you have an idea how to make the conversation accessible for both users? (it is accessible but not in the way i need it to).

thanks in advance!

1

1 Answers

2
votes

Your user association with conversations is only looking for sender_id

has_many :conversations, :foreign_key => :sender_id, dependent: :destroy

The recipient user is looking for conversation with his id as sender_id and not as recipient_id.

I think you can fix that by adding a join model between users and conversations instead of a conversation just belonging to a sender and a recipient (though you should save those references anyway).

Something like

class ConversationsUsers < ....
  belongs_to :user
  belongs_to :conversation
end

class User < ...
  has_many :conversations_users
  has_many :conversations, through: :conversations_users
  has_many :started_conversations, class_name: 'Conversation', foreign_key: :sender_id
  has_many :received_conversations, class_name: 'Conversation', foreign_key: :recipient_id
end

class Conversation < ...
  has_many :convesations_users
end

For each Conversation you'll have two records on conversations_users table, one for each user. Now both users can query all the conversations independant of being a sender or a recipient (and you can have any number of participants on a conversation too if you plan to extend it!).

You'll have to create some records manually though, after creating a Conversation you could have an after callback to create two conversation_user object one for each user.


Another option is to just use a method

def conversations
  Conversation.where(sender_id: id).or(Conversation.where(recipient_id: id))
end

but you won't be able to use joins/includes/etc.

EDIT: To get all messages, add another method:

def messages
  conv_ids = conversations.pluck(:id)
  Message.where(conversation_id: conv_ids)
end

then you can this to get all read messages for example

current_user.messages.where(read: true)