Как получить сводные данные внутри красноречивого обратного вызова функции withCount?

Я пытаюсь использовать сводную переменную родительского отношения внутри красноречивого метода withCount().

Задний план:

Существует приложение с отношением ManyToMany между пользователями и кластерами. Пользователи могут отправлять сообщения внутри кластера. Чтобы отслеживать количество непрочитанных сообщений для пользователя в определенном кластере, я отслеживаю идентификатор последнего прочитанного сообщения в таблице соединений, например:

table: cluster_user

cluster_id | user_id | last_read_message_id
-------------------------------------------
 1         | 59      | 3
 2         | 62      | 8
  • Модель User() имеет связь belongsToMany() с моделью Cluster().
  • Модель Cluster() имеет связь belongsToMany() с моделью User().
  • Модель Cluster() имеет связь hasMany() с моделью Messages().
  • Модель Message() имеет связь belongsTo() с моделью Cluster().

Теперь я хотел бы перечислить все кластеры аутентифицированного пользователя, включая количество непрочитанных сообщений.

В настоящее время я застрял на этом:

$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
  $query->where('messages.id', '>', '???');
}])->get();

Я уже пробовал:

$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
  $query->where('messages.id', '>', 'cluster_user.last_read_message_id');
}])->get();

Но это дает мне общее количество всех сообщений вместо сообщений с идентификатором выше x. Если я жестко закодирую идентификатор, например:

$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
  $query->where('messages.id', '>', '3');
}])->get();

Затем я получаю правильный счетчик непрочитанных сообщений.

Так может ли кто-нибудь сказать мне, как использовать сводную переменную 'last_read_message_id' отношения user()->cluster() внутри функции обратного вызова withCount(), имея в виду следующее:

  • Крайне важно использовать как можно меньше запросов.
  • Счетчик непрочитанных сообщений должен быть частью коллекции cluster(), потому что позже я возвращаю ClusterResource, например:

return ClusterResource::collection($clusters);

который включает количество непрочитанных сообщений.

class ClusterResource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'name' => $this->name,
            'unread_messages_count' => $this->whenPivotLoaded('cluster_user', $this->messages_count)
        ];
    }
}

Спасибо!

Вчера я получил в основном тот же вопрос, и ответ был: вы не можете .. :( stackoverflow.com/questions/54444858/… Если вы найдете способ, мне все равно будет интересно.

Clément Baconnier 01.02.2019 13:54

@cbaconnier интересно! и грустно :( Хотя, когда я вывожу getQueryLog(), я получаю следующий запрос: select 'clusters'.*, (select count(*) from 'messages' where 'clusters'.'id' = 'messages'.'cluster_id' and 'messages'.'id' > ?) as 'messages_count', blablabla from 'clusters' inner join 'cluster_user' on 'clusters'.'id' = 'cluster_user'.'cluster_id' where 'cluster_user'.'user_id' = ? (заменил кусок на blablabla, чтобы сократить его). Если я заменю первый вопросительный знак на cluster_user.last_read_message_id, результат будет правильным, так что это не кажется быть совершенно отдельно в этом случае!

Jouke 01.02.2019 14:05

@cbaconnier нашел решение благодаря вашему комментарию! DB::raw('cluster_user.last_read_message_id') у меня работает.

Jouke 01.02.2019 14:08

Ууууу! тогда я рада за вас :)

Clément Baconnier 01.02.2019 14:10

я тоже посмотрю на твой вопрос

Jouke 01.02.2019 14:12
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
3
5
1 480
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Хороший вопрос! Я думаю, вы должны иметь возможность использовать метод withPivot в своих отношениях, а затем использовать атрибут pivot в своем запросе.

Например, в вашей модели Cluster, где вы определяете связь cluster_user, выполните следующие действия:

function cluster_user() {
    return $this->belongsToMany('App\User')
                ->withPivot('last_read_message_id');
}

А затем в своем запросе вы можете использовать whereColumn для сравнения столбцов. Что-то вроде этого:

$clusters = Auth::user()
    ->clusters()
    ->withCount(['messages' => function ($query) {
       $query->whereColumn('messages.id', '>',  
                           'pivot.last_read_message_id');
     }])
    ->get();

Найдите «Извлечение столбцов промежуточной таблицы» в Документация по красноречивым отношениям для получения дополнительной информации.

Спасибо @Delena Malan за ваш ответ, но отношение в модели Cluster уже объявлено следующим образом: return $this->belongsToMany(User::class)->withPivot('last_read_mess‌​age_id');

Jouke 01.02.2019 14:09

Ах, да, я только что понял, что он будет принимать значение как pivot.last_read_message_id вместо фактического значения столбца. Вы можете поиграть с whereColumn. Он должен работать аналогично DB::raw.

D Malan 01.02.2019 14:20
Ответ принят как подходящий

Нашел ответ благодаря комментарию @cbaconnier.

Размещение DB::raw('cluster_user.last_read_message_id') на месте работает. Я не опрятный, но это работает.

Полный пример:

$clusters = Auth::user()
     ->clusters()
     ->withCount(['messages' => function ($query) {
            $query->where('messages.id', '>', DB::raw('cluster_user.last_read_message_id'));
     }])
     ->get();

Другие вопросы по теме

Похожие вопросы

Ошибка синтаксического анализа PHP: синтаксическая ошибка, неожиданное «Y9gG??» (T_STRING). Внезапно появляется на изображениях и в таблицах стилей
Я хочу отфильтровать те же отображаемые данные в моей таблице данных
Вызов встроенного фильтра в пользовательском фильтре Twig
Разница между двумя массивами в php
Как использовать динамические данные в качестве параметра с функцией date_create_from_format в PHP
Выбор запроса mysqli, например, запрос уведомления, выбор из четырех других таблиц, которые ограничены определенным значением ключа и возвращают единственный результат
Php самый эффективный поиск массива объектов по полю объекта
Нужно регулярное выражение, которое оставило бы только имя файла в src для img
Получение избранного изображения публикации на другой странице
Session_start() в конструкторе класса сеанса (несколько сеансов запускаются для каждого URL-адреса или нет)