SELECT
subquery.opponent_id,
subquery.last_message_datetime,
m.message AS last_message
FROM (
SELECT
CASE
WHEN m.from_user_id = 4 THEN m.to_user_id
ELSE m.from_user_id
END AS opponent_id,
MAX(m.datetime) AS last_message_datetime
FROM messages AS m
WHERE m.from_user_id = 4 OR m.to_user_id = 4
GROUP BY opponent_id
) AS subquery
LEFT JOIN messages AS m ON (
(m.from_user_id = 4 AND m.to_user_id = subquery.opponent_id)
OR
(m.to_user_id = 4 AND m.from_user_id = subquery.opponent_id)
) AND m.datetime = subquery.last_message_datetime
WITH
`cte1` AS (
SELECT `to_user_id` AS `respondent`, `message`, `datetime`,
'outgoing' AS `direction`
FROM `messages`
WHERE `from_user_id` = :userId
UNION ALL
SELECT `from_user_id`, `message`, `datetime`,
'incoming' AS `direction`
FROM `messages`
WHERE `to_user_id` = :userId
),
`cte2` AS (
SELECT `respondent`, `message`, `datetime`, `direction`,
ROW_NUMBER() OVER `w` AS `row_num`
FROM `cte1`
WINDOW `w` AS (
PARTITION BY `respondent`
ORDER BY `datetime` DESC
)
)
SELECT `respondent`, `message`, `datetime`, `direction`
FROM `cte2`
WHERE `row_num` = 1