1

I try to build simple conversation system for my app, but I have some troubles with planning that structure. When user go to 'conversation' page it should list all conversation where the user was sender or receiver with order by the newest messages. Then, when user open specific conversation it should list all messages from that conversation. I planned something like that:

  1. Create Conversation table

            $table->increments('id');            
            $table->integer('sender_id');
            $table->integer('receiver_id');
            $table->timestamps();
    
  2. Create Messages table

            $table->increments('id');
            $table->text('message');
            $table->integer('conversation_id');
            $table->integer('user_id');
            $table->timestamps();
    
  3. Create Conversation and Message Model

  4. User Model - ?

    public function conversations()
    {
        return $this->hasMany('App\Conversation', 'sender_id');
    }
    

Here I have troubles - I want to make relation with conversations where foreign key will be 'sender_id' or 'receiver_id' and return conversation order by the newest messages. How can I accomplish that? Can you give me some tips how solve relations between users, conversations and messages to return all information when I return user?

I really appreciate any help.

Morgan
  • 140
  • 16
  • Possible duplicate of [Laravel merge relationships](https://stackoverflow.com/questions/24184069/laravel-merge-relationships) – levi Mar 08 '19 at 17:18

2 Answers2

3

First of all, you should define the Conversation relationship with messages like this:

Conversation Model

public function messages()
{
  return $this->hasMany('App\Message');
}

You should define the inverse relationship as well:

Message Model

public function conversation()
{
  return $this->belongsTo('App\Conversation');
} 

You could show all conversations by a user with the code below:

public function getConversations($userId)
{
  $conversations = Conversation::where('sender_id',$userId)->orWhere('receiver_id',$userId);
  return view('yourview', compact('conversations'));
}

In your view you could loop and find every message from every conversation:

@foreach($conversations as $conversation)
 @foreach($conversation->messages as $message)
  {{$message->message}}
 @endforeach
@endforeach

In your conversations table you could have a user_id FK, also your relation should look like this:

User Model

public function conversations()
{
    return $this->hasMany('App\Conversation', 'user_id');
}

Note: you could use the receiver_id or sender_id as a foreign key as well.

With this relation you could get all conversations from a user with this:

$user = User::find($id);
$user->conversations; // This will return all conversations from that user

You could also get all the messages from a user using the Has Many Through relationship:

public function messages()
{
 return $this->hasManyThrough('App\Message', 'App\Conversation');
}


Another Approach

Another way to do it is creating a pivot table between users and conversations

The conversation_user table

id 
user_id 
conversation_id

This way a user could have many conversations (Many to many relationship).

You could change your messages table to:

id
conversation_id
receiver_id
sender_id
content 
...

Your Message Model

public function conversation()
{
 return $this->belongsTo('App\Conversation', 'conversation_id');
}

The User model

public function conversations()
{
    return $this->belongsToMany('App\Conversation');
}

The Conversation Model

public function users()
{
 return $this->belongsToMany('App\User');
}

public function messages()
{
  return $this->hasMany('App\Message');
}

I think this is the best way to do it

If you wanna get the conversation and the messages:

$user = User::find($id);
foreach($user->conversations as $conversation)
{
 foreach($conversation->messages as $message)
 {
   echo $message->content;
 }
}

Another simple way to do it is using the Message Model:

$userMessages = Message::where('receiver_id', $userId)->orWhere('sender_id',$userId)->get();

But this way you will only get the messages, you can find the conversation with this:

foreach($userMessages->conversation as $conversation)
{
 echo $conversation;
}
Piazzi
  • 2,490
  • 3
  • 11
  • 25
  • 1
    What a great and informative answer - thank you so much. I don't understand that 'In your conversations table you should have a user_id FK'. So I need to make sender_id, receiver_id and user_id in my conversation table? – Morgan Mar 08 '19 at 17:58
  • Yes, i think it's the best way to do it, because a conversation belongs to a user or many users, right? So you could define a foreign key for that, but you dont need to do that if you dont want, you decide. As a said, you can get all the info you need it just using the query i described above. I'm glad that i could help, good luck on your studies. If you think my answer helped you with your problem, please consider accepting it. – Piazzi Mar 08 '19 at 19:45
  • I updated my answer and made a better aproach to your problem, take a look – Piazzi Mar 08 '19 at 19:58
  • Thank you for your help. I used your 'another approach'. I have only one question. Now when I return logged in user i add ->with('conversation') and it returned array "conversations": [ { "id": 1, "created_at": null, "updated_at": null, "pivot": { "user_id": 11, "conversation_id": 1 } } ]. How can I get more info from pivot table and messages table at one request? – Morgan Mar 09 '19 at 08:22
0

My approach would be: I have conversations that have a subject. Every message has a conversation_id and a user_id that sends the message. and finally, in participants table, I would add the users that are part of the thread but not necessarily send a message in it.

So you must have a conversations table with just one subject, You must have participants table so that you must add users that part of a conversation. and finally, a messages table that you must save every message that user sends to a conversation.

for threads table:

Schema::create('conversations', function (Blueprint $table) {
    $table->increments('id');
    $table->string('subject');
    $table->timestamps();
});

for messages table:

Schema::create('messages', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('conversation_id')->unsigned();
    $table->integer('user_id')->unsigned();
    $table->text('body');
    $table->timestamps();
});

for any user that participates in the message:

Schema::create('participants', function (Blueprint $table) {
    $table->increments('id');
    $table->integer('conversation_id')->unsigned();
    $table->integer('user_id')->unsigned();
    $table->timestamp('last_read')->nullable();
    $table->timestamps();
});

User Model:

public function conversations()
{
    return $this->belongsToMany('App\Conversation', 'user_id');
}

Conversation Model:

public function messages()
{
    return $this->belongsToMany('App\User', 'conversation_id');
}
Salar Bahador
  • 1,433
  • 1
  • 14
  • 26