#laravel
Вопрос:
Есть ли аналогичное «Где» для «Где между» в Laravel 7 ? У меня есть базовое приложение для чата:
Модель сообщений
идентификатор, содержимое, идентификатор пользователя, идентификатор потока
threadId
заполняется, когда пользователь отвечает на сообщение. Подумайте «родительский».
public function sentBy(): BelongsTo
{
return $this->belongsTo(User::class, 'userId');
}
public function recipients(): HasMany
{
return $this->hasMany(MessageRecipient::class);
}
Модель получателей
идентификатор, идентификатор пользователя, идентификатор сообщения
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'userId');
}
public function message(): BelongsTo
{
return $this->belongsTo(Message::class, 'messageId');
}
Тогда я мог бы сказать:
User::first()->messages;
Message::first()->sentBy;
При такой настройке recipients
модель можно легко назвать «групповой». Когда пользователь создает сообщение, recipients
связь создается для текущего пользователя вместе с другими пользователями независимо от того, было ли сообщение отправлено тому, кто его создал. Обычное? ОК. Моя проблема в том, что мне нужно вернуть весь разговор между нами тремя идентификаторами пользователей: [1,2,3]
:
$messageId = 1;
// Message with id of 1 will not have a threadId, only its replies.
$userIds = [1,2,3];
foreach($userIds as $id) {
Recipient::create(['messageId' => $messageId, 'userId' => $id]);
}
We have a «group chat theme» going on here. If I need to query for message with us three users (above userIds), I’d do:
return Message::whereHas('recipients', function(Builder $q) use ($userIds) {
return $q->whereIn('userId', $userIds);
})
->orderBy('id', 'DESC')
->get();
Идеальный. Именно то, что мне нужно. Теперь давайте создадим еще одно сообщение:
$messageId = 2;
$userIds = [1,2];
$queryUserIds = [2,3];
foreach($userIds as id) {
Recipient::create(['messageId' => $messageId, 'userId' => $id]);
}
// With this query, I'd expect to see message between the two queryUserIds **only**,
// Expected results should be an empty array because we have no message between userId 2 and 3 only. We have messages between all three: 1,2,3.
return Message::whereHas('recipients', function(Builder $q) use ($queryUserIds) {
return $q->whereIn('userId', $queryUserIds);
})
->orderBy('id', 'DESC')
->get();
Как я могу запрашивать сообщения между набором идентификаторов пользователей. Не уверен, что правильно выражаюсь. Все, что я хочу сказать, для пользователей 1, 2 и 3, дайте мне беседы только с этими тремя пользователями и ничего больше, потому что только эти трое ведут один и тот же разговор.
Я получил эту идею из мессенджера Facebook. Я не знаю, как они это делают, но я «заимствую» функциональность; когда вы создаете новое сообщение, оно, похоже, извлекает последние/последние разговоры между теми же «получателями». Я знаю, что есть и более сложные вещи, но я хотел сделать что-то простое.
Ответ №1:
Думаю, я нашел его. Поправьте меня, если я ошибаюсь. Напомним, что при создании сообщения создается массив получателей с сообщением.ThreadId, равным нулю. Когда кто-нибудь ответит на это сообщение, теперь у вас будет message.threadId
1. Когда мне нужны сообщения между всеми тремя пользователями: 1,2,3
, я бы запросил:
$userIds = [1,2,3];
// Probably could use sql's sum but... 🤷🏽
$sumIds = array_sum($userIds);
$messages = Message::whereHas('recipients', function(Builder $q) use ($sumIds) {
// SUM is the key part.
return $q->havingRaw('SUM(recipients.userId) ='. $sumIds);
})->get();
Это работает на 99%, но мне нужны «ответы», сообщения с threadId
ненулевым значением; Мне нужны ответы:
$message = Message::whereHas('recipients', function(Builder $q) use ($sumIds) {
return $q->havingRaw('SUM(recipients.userId) ='. $sumIds);
})
// I literally need the first in the array. I'd expect the array should contain one object and never more than one
->first();
if (!$message) {
return [];
}
// Another query to get all (conversations). You'll now see the picture:
return Message::where(function($q) use ($message) {
$q->orWhere('id', $message->messgeId);
// This gives me the "replies"
$q->orWhere('threadId', $message->messgeId);
})->get();
Пока это работает. Если кто-нибудь сможет провести рефакторинг, я с радостью приму ваш ответ с повышением.
Спасибо!