GROUP BY GROUP_CONCAT для объединения сообщений в одну строку, упорядоченную по времени т.е если у тебя есть поле timestamp или msg_time если ты не хочешь использовать chat_id, то можно попробовать сделать следующий запрос
GROUP BY chat_id - таким образом, строки с одинаковым chat_id будут объединены в одну группу
"Функция LEAST выбирает минимальное из значений. Значениями могут выступать поля, а также строки и числа"
"Функция GREATEST выбирает максимальное из значений / помогает упорядочивать пары пользователей"
Комбинация LEAST и GREATEST гарантирует уникальность групировки чатов игнорируя порядок пар пользоваталей чатов.
Вобщем есть набросок, дальше можно уже самому накидать и поиграться.
$sql = "
SELECT
MIN(from_user_id) as user1,
MAX(from_user_id) as user2,
chat_id,
GROUP_CONCAT(msg ORDER BY msg_time) as messages
FROM
class_chat
WHERE
(from_user_id = :user1 AND to_user_id = :user2)
OR
(to_user_id = :user1 AND from_user_id = :user2)
OR
(from_user_id = :user3 AND to_user_id = :user4)
OR
(to_user_id = :user3 AND from_user_id = :user4)
GROUP BY
chat_id, LEAST(from_user_id, to_user_id), GREATEST(from_user_id, to_user_id)
";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':user1' => 111, // тут айдишники пользаков
':user2' => 222,
':user3' => 333,
':user4' => 444,
]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
// Обработка результата
echo "Chat ID: " . $row['chat_id'] . "\n";
echo "User 1: " . $row['user1'] . "\n";
echo "User 2: " . $row['user2'] . "\n";
echo "Messages: " . $row['messages'] . "\n";
echo "-----------------\n";
}