Search code examples
laravellaravel-query-builder

Laravel orderby joining 2 tables without eloquent relationship


I have 2 tables for a chatting system

"users" table structure:

id name

"messages" table structure:

id sender_id receiver_id message

I want to return friend users ordered by when there was chatting between you and friend last time. Note: id works same as timestamp

Suppose,

id sender_id receiver_id message
1 $userA->id $userB->id abc
2 $userC->id $userA->id xyz

So, If $userA is the logged in user - I want users to be returned like:

[
    0 => $userC,
    1 => $userB,
    2 => $userD,
    3 => $userE,
    4 => $userF,
    ...
]

(where $userD, $userE, $userF has no messages with user $userA)

Basically I want to return all the friend users sorted using if user has any message with logged in user.

My current code giving me some unexpected result -

return DB::table('users')->leftJoin('messages', function ($join) {
    $join->on('users.id', '=', 'messages.sender_id')
        ->where('messages.sender_id', auth()->id())
        ->orOn('users.id', '=', 'messages.receiver_id')
        ->where('messages.receiver_id', auth()->id());
})
->select('users.*')
->where('users.id', '!=', auth()->id())
->orderBy('messages.id', 'asc')
->get();

Solution

  • Honestly I can't be sure if this will work as I can't testi it, but I think code should be something like:

    $messagesSent = DB::table('messages')
        ->select('sender_id as other_user', DB::raw('MAX(id) as last_message_id'))
        ->where('receiver_id', auth()->id())
        ->groupBy('sender_id');
    
    $messagesSentOrReceived = DB::table('messages')
        ->select('receiver_id as other_user', DB::raw('MAX(id) as last_message_id'))
        ->where('sender_id', auth()->id())
        ->groupBy('receiver_id')
        ->union($messagesSent);
    
    $usersWithCommunication = DB::table('users')
        ->select('other_user', DB::raw('MAX(last_message_id) as last_message_id'))
        ->joinSub($messagesSentOrReceived, 'latest_messages', function ($join) {
            $join->on('users.id', '=', 'latest_messages.other_user');
        })
        ->groupBy('other_user');
        
    
    return DB::table('users')
        ->leftJoinSub($usersWithCommunication, 'latest_communications', function ($join) {
            $join->on('users.id', '=', 'latest_communications.other_user');
        })
        ->select('users.*')
        ->orderBy('latest_communications.last_message_id', 'asc')
        ->get();