Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

shadrix's avatar
Level 12

Get the latest Message of Chat Model with MySQL. Just cannot get the idea how to do this.

Sorry that I have to ask such a noobie question, but I learned SQL about 8 years ago and never used anymore.

It should be quite simple, if you look at this current database: https://imgur.com/a/hSF1M

So my goal is to get from that for example with sender_id = 1

 id    sender_id   receiver_id   content    read   created_at     
 3        1            2        Freut mich    0     2018-01-15 20:09:19
 5        1            4        Hey Jake     0     2018-01-16 18:09:19 (sorry had a timestamp mistake)

So I know the sender_id, but I want I just need the latest created_at message with it's content.

My first approach was old school SQL but I stuck:

 SELECT DISTINCT sender_id, receiver_id FROM chats Where sender_id = 1 

I hope it's understandable what I want. Thank YOU for your help.

0 likes
13 replies
sutherland's avatar

Assuming you have an Eloquent model name ChatMessage, this should work:

$message = ChatMessage::latest()->where('sender_id', 1)->get();
shadrix's avatar
Level 12

@sutherland That would be easy but don't forget it is >where('sender_id', 1)->orWhere('receiver_id',1)

But I would get ALL messages back, but I just want to get the last one.

So in your case I didn't get the receiver and I got a message that is too old.

sutherland's avatar

Oops, you can change ->get() to ->first()

Edit: So the complete thing would be:

$message = ChatMessage::latest()->where('sender_id', 1)->orWhere('receiver_id', 1)->first();
shadrix's avatar
Level 12

Thank you for your fast reply, but still no @sutherland. First() get's me only one result

#original: array:7 [▼
"id" => 5
"sender_id" => 1
"receiver_id" => 4
"content" => "Hey Jake"
"read" => 0
"created_at" => "2018-01-16 18:09:19"
"updated_at" => "2018-01-16 18:09:19"
]

But I expect of course Row ID 2 as well. So not just one.

To explain it again, it can be with id 1

 sender_id receiver_id
      1         2
      3         1
      1         4
     12         1

Should get me four results

sutherland's avatar

I think I see what you're asking now, and I think it would require a fairly complex query. My first instinct would be to try using groupBy() but then you'll still get two messages for each conversation: the latest where the user is the sender and the latest where the user is the receiver.

If it's not impossible to change your schema I'd recommend adding a Conversation model and make it so that a message belongs to a sender and to a conversation, and use a many to many relationship to assign users to a conversation.

shadrix's avatar
Level 12

Thank you @sutherland! I've to think about it!

Meanwhile I found a GitHub Project that contained the similar idea: https://github.com/felicianopj/laravel-vue-chat/blob/master/app/Http/Controllers/ChatController.php

But the thing is, that as I can see they don't have a Inbox View of the last messages of all users to one user (as you might see on the first page of the twitter Messages Modal)

Thank you anyway, if I find a solution I will post it here

shadrix's avatar
Level 12

So I got a solution, but I'm not happy about it. It's pretty complicated SQL but I wish it would be possible to do it with Eloquent. Right now I'm not sure how to do this.

Here is my current solution:

$id = auth()->id();
    $inbox = DB::select('
        SELECT t1.*
        FROM chats AS t1
        INNER JOIN
        (
            SELECT
                LEAST(sender_id, receiver_id) AS sender_id,
                GREATEST(sender_id, receiver_id) AS receiver_id,
                MAX(id) AS max_id
            FROM chats
            GROUP BY
                LEAST(sender_id, receiver_id),
                GREATEST(sender_id, receiver_id)
        ) AS t2
            ON LEAST(t1.sender_id, t1.receiver_id) = t2.sender_id AND
               GREATEST(t1.sender_id, t1.receiver_id) = t2.receiver_id AND
               t1.id = t2.max_id
            WHERE t1.sender_id = ? OR t1.receiver_id = ?
        ', [$id, $id]);
6 likes
braian125's avatar

Until today, it is the best solution I have found. Thank you so much

hridoy100's avatar

until this day, it is the best solution I've found!!

Binay7587's avatar

Would be better if someone could convert this into Eloquent

1 like
cliftonscott's avatar

Here is how I did it.

Message::withTrashed()
            ->from('messages as m1')
            ->select('m1.*')
            ->join(DB::raw(
                '(
                SELECT
                    LEAST(from_id, to_id) AS from_id,
                    GREATEST(from_id, to_id) AS to_id,
                    MAX(id) AS max_id
                FROM ' . $this->table . '
                GROUP BY
                    LEAST(from_id, to_id),
                    GREATEST(from_id, to_id)
            ) AS m2'
            ), fn($join) => $join
                ->on(DB::raw('LEAST(m1.from_id, m1.to_id)'), '=', 'm2.from_id')
                ->on(DB::raw('GREATEST(m1.from_id, m1.to_id)'), '=', 'm2.to_id')
                ->on('m1.id', '=', 'm2.max_id'))
            ->where('m1.from_id', $this->user->id)
            ->orWhere('m1.to_id', $this->user->id)
            ->orderByDesc('m1.created_at');
1 like

Please or to participate in this conversation.