В общем ключе вот так -
SELECT * FROM
(SELECT max(id) as maxId, from_id
GROUP BY from_id
WHERE from_id in (1,2,3)
ORDER BY maxId desc
) as t1
INNER JOIN messages m on t1.maxId = m.id
ЧатГПТ Laravel далее код сгенерировал, выглядит корректно. Заметьте, что первую часть запроса в любом случае выполнять через Query Builder, но можно вывести ее в получение конкретных id последних сообщений. И дальше двумя, можно отдельными запросами уже через модель - получить данные по сообщениям и пользователям. Они будут работать по первичным ключам и срабатывать моментально.
А можно просто добавить жадную загрузку по связи Users на втором запросе, что в целом облегчит вам задачу и не нужно будет связывать далее сообщение и его автора.
// Подзапрос для получения maxId и from_id
$subQuery = (new Query())
->select(['maxId' => 'MAX(id)', 'from_id'])
->from('messages')
->where(['from_id' => [1, 2, 3]])
->groupBy('from_id')
->orderBy(['maxId' => SORT_DESC]);
// Основной запрос с INNER JOIN
$query = (new Query())
->select('*')
->from(['t1' => $subQuery])
->innerJoin('messages m', 't1.maxId = m.id');
// Выполнение запроса
$results = $query->all();