Как запросить сообщения между точными получателями?

#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();

 

Пока это работает. Если кто-нибудь сможет провести рефакторинг, я с радостью приму ваш ответ с повышением.

Спасибо!