Я создаю функцию чата в нашем приложении. Основной чат c работает, у нас был запрос, чтобы получить разговоры, принадлежащие пользователю, получить разговор, сообщения и т. Д. c.
Теперь мы хотим добавить функцию, в которой участник разговор (разговоры могут иметь несколько участников) может удалить чат на их конце, но этот не удалит разговор на сервере. Вместо этого мы помечаем беседу как удаленную из точки Х для этого пользователя. В этом случае, когда участник, который удалил беседу, снова запрашивает диалог в нашем API, он не увидит сообщения до своего удаления.
Чтобы ясно понять концепцию, это то же самое, что WhatsApp, Telegram или большинство приложений чатов работают в настоящее время. Когда пользователь A и B взаимодействуют, если пользователь B решает удалить разговор со своего телефона, пользователь A по-прежнему будет видеть весь разговор. Если пользователь B (или A) снова отправит текстовые сообщения в беседе, пользователь B увидит только новые тексты.
Я не совсем уверен, как это работает на их концах, но структура, которая, кажется, работает для us следующий:
CREATE TABLE `conversations` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`starter_id` bigint(20) unsigned NOT NULL,
`last_message_id` bigint(20) unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `conversations_starter_id_index` (`starter_id`),
KEY `conversations_last_message_id_index` (`last_message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `conversation_participants` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`conversation_id` bigint(20) unsigned NOT NULL,
`participant_id` bigint(20) unsigned NOT NULL,
`deleted_from_id` bigint(20) unsigned DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `conversation_participants_conversation_id_index` (`conversation_id`),
KEY `conversation_participants_participant_id_index` (`participant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `conversation_messages` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`conversation_id` bigint(20) unsigned NOT NULL,
`sender_id` bigint(20) unsigned NOT NULL,
`message` text COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `conversation_messages_conversation_id_index` (`conversation_id`),
KEY `conversation_messages_sender_id_index` (`sender_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Как вы видите в conversation_participants
, мы добавили deleted_from_id
. Это deleted_from_id
будет обновлено, когда пользователь B отправит запрос на сервер, чтобы удалить диалог. Он отправит разговор / идентификатор и последний разговор, который он видел, и обновит соответственно.
Мы используем Laravel в качестве основы и Eloquent для простой генерации запросов со связями. У нас есть конечная точка, которая запрашивает последние 25 разговоров для пользователя, а затем мы разбиваем их на страницы. Это запрос, сгенерированный для этого типа запроса:
select
`conversations`.*,
`conversation_participants`.`participant_id` as `pivot_participant_id`,
`conversation_participants`.`conversation_id` as `pivot_conversation_id`,
`conversation_participants`.`created_at` as `pivot_created_at`,
`conversation_participants`.`updated_at` as `pivot_updated_at`
from
`conversations`
inner join `conversation_participants` on `conversations`.`id` = `conversation_participants`.`conversation_id`
where
`conversation_participants`.`participant_id` = 1
and exists (
select
*
from
`conversation_messages`
where
`conversations`.`id` = `conversation_messages`.`conversation_id`
)
order by
`id` desc
Приведенный выше запрос довольно прост, он возвращает диалоги для определенного пользователя c. Используя Laravel Conversation::with('messages')...
, это позволяет нам легко фильтровать разговоры, в которых есть сообщения (мы не хотим, чтобы пустые разговоры возвращались).
Проблема в том, что мы пытались фильтровать больше и предотвращать разговоры что пользователь удалил на своем телефоне, чтобы показать в этом запросе. Мы не нашли способ.
Нашей первой догадкой было просто добавить настройку exists()
, ограничивающую conversation_messages.id
, например:
select
`conversations`.*,
`conversation_participants`.`participant_id` as `pivot_participant_id`,
`conversation_participants`.`conversation_id` as `pivot_conversation_id`,
`conversation_participants`.`deleted_from_id` as `pivot_deleted_from_id`,
`conversation_participants`.`created_at` as `pivot_created_at`,
`conversation_participants`.`updated_at` as `pivot_updated_at`
from
`conversations`
inner join `conversation_participants` on `conversations`.`id` = `conversation_participants`.`conversation_id`
where
`conversation_participants`.`participant_id` = 1
and exists (
select
*
from
`conversation_messages`
where
`conversations`.`id` = `conversation_messages`.`conversation_id`
and `id` > conversation_participants.deleted_from_id
)
order by
`id` desc
Это будет "работать", но если пользователь удалил сообщение и, скажем, есть разговоры с сообщениями старше and
id > conversation_participants.deleted_from_id
, другие разговоры не будут возвращены. Это неверно, так как предотвращает показ любого другого разговора, даже если у него есть сообщения и он принадлежит участнику.
Мы также попробовали другой подход, используя некоторые объединения в exists()
, чтобы попытаться предотвратить "" удаленный разговор »для отображения в списке:
select
`conversations`.*,
`conversation_participants`.`participant_id` as `pivot_participant_id`,
`conversation_participants`.`conversation_id` as `pivot_conversation_id`,
`conversation_participants`.`deleted_from_id` as `pivot_deleted_from_id`,
`conversation_participants`.`created_at` as `pivot_created_at`,
`conversation_participants`.`updated_at` as `pivot_updated_at`
from
`conversations`
inner join `conversation_participants` on `conversations`.`id` = `conversation_participants`.`conversation_id`
where
`conversation_participants`.`participant_id` = 1
and exists (
select
`conversation_messages`.*
from
`conversation_messages`
join
`conversations` on `conversations`.`id` = `conversation_messages`.`conversation_id`
join
`conversation_participants` on `conversations`.`id` = `conversation_participants`.`conversation_id`
where
`conversations`.`id` = `conversation_messages`.`conversation_id`
and `conversation_participants`.`participant_id` = 1
and `conversation_messages`.`id` > `conversation_participants`.`deleted_from_id`
)
order by
`id` desc
Но, к сожалению, это тоже не сработало.
Чтобы сделать тестирование более удобным, я настроил здесь БД Fiddle: https://www.db-fiddle.com/f/q6S3GfZNCbvYbtvRwXJxN7/0
Эта скрипка имеет несколько пользователей, несколько разговоров и несколько сообщений. Как видите, если вы выполните запрос сразу же, он вернет 28 разговоров, принадлежащих данному участнику.
При этом в таблице разговор_причастников есть строка для member_id = 1, которая в файлеословия_вопроса = 82 сообщение об удалении - это 82: (55,28,1,82,'2020-01-31 10:01:08','2020-01-31 10:01:08'),
(строка 166 в скрипке). Сообщение 82 является последним сообщением в chat_id = 28, поэтому оно не должно отображаться в запросе, поскольку в нем нет сообщений.
В наших попытках найти решение мы также подумал, что, возможно, наличие строки conversations.last_message_id
будет полезно для предотвращения появления разговоров ... но мы также не уверены в этом, поскольку не можем найти решение. Я решил оставить его в SQL на случай, если будет полезно найти решение.
Как я могу получить желаемые результаты? Что я упускаю?
Заранее спасибо