0

I created a private messages system but I am having trouble with the inbox. I want to create a regular inbox where you can see all your conversations. I tried to do something but I get a result of all the messages the user got but I want to display only the last message from each conversation.

SQL: I got messages table with id, user_id(int), to_id(int), content(text), read(boolean).

Messages Controller:

def inbox
@messages = Message.where("to_id = ? OR user_id = ? AND to_id != 0", current_user, current_user).order(created_at: :desc)
end

View:

<% @messages.each do |message| %>
    <% to_user_id = User.find(message.to_id) %>
    <% to_user_name = to_user_id.username %>
    <b><p><%= to_user_name %></p></b>
    <p>
    <% if message.read == false %>
      <b><%= link_to message.content, pm_path(to_user_id) %></b>
    <% else %>
      <%= link_to message.content, pm_path(to_user_id) %>
    <% end %>
    </p>
  <% end %>

Hope I am clear and thank you in advance.

Omer TaB
  • 1
  • 3

2 Answers2

0

try this one

@messages = Message.find_by("(to_id = ? OR user_id = ?) AND to_id != 0", current_user, current_user).order(created_at: :desc)
Stephen M
  • 182
  • 2
  • 16
0

If you setup the associations properly it is trivial:

class User < ApplicationRecord
  has_many :recieved_messages, class_name: 'Message', foreign_key: 'recipent_id'
  has_many :sent_messages, class_name: 'Message', foreign_key: 'sender_id'

  def all_messages
    Message.where('recipient_id = :id OR sender_id = :id', id: id)
  end
end

class Message < ApplicationRecord
  belongs_to :recipient, class_name: 'User', inverse_of: :recieved_messages
  belongs_to :sender, class_name: 'User', inverse_of: :sent_messages

  def self.between(a,b)
    .where('(recipient_id = :id OR sender_id = :id)', id: a)
    .where('(recipient_id = :id OR sender_id = :id)', id: b)
  end
end

# all messages recieved by the current user:
current_user.recieved_messages.order(created_at: :desc)

# all messages recieved by the current user:
current_user.sent_messages.order(created_at: :desc)

# all messages sent or recieved by the current user:
current_user.all_messages.order(created_at: :desc)

# all messages between two users
Message.between(current_user, some_other_user)

The key to getting any message between to users is:

WHERE (recipient_id = a OR sender_id = a) AND (recipient_id = b OR sender_id = b)

But you should consider creating a conversation model to join them instead.

Calling .last will get the oldest record since it is in descending order. If you want the newest message use .first

To avoid a N+1 query issue make sure you load the associated records:

@messages.eager_load(:recipient, :sender)

When iterating (an in general) use the associations instead of ids. Never do stuff like User.find(message.to_id) as this will cause a N+1 query issue.

<% @messages.each do |message| %>
  <p><b><%= message.recipent.name %></b></p>
  <p>
    <% if message.read? %>
      <%= link_to message.content, pm_path(message.recipient) %>
    <% else %>
      <b><%= link_to message.content, pm_path(message.recipient) %></b>
    <% end %>
  </p>
<% end %>
max
  • 96,212
  • 14
  • 104
  • 165