Morgan
Morgan

Reputation: 140

Laravel: building my own conversation system

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.

Upvotes: 1

Views: 734

Answers (2)

Piazzi
Piazzi

Reputation: 2636

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;
}

Upvotes: 3

Salar Bahador
Salar Bahador

Reputation: 1494

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');
}

Upvotes: 0

Related Questions