Laravel — Элементы запроса БД на основе состояния сводной таблицы

#php #mysql #laravel #eloquent #left-join

Вопрос:

У меня в приложении есть список модулей, которые игроки могут выполнить, и когда они это сделают, они должны перестать появляться в списке «доступные модули». У меня есть таблица «игроки», «модули» и «module_player», чтобы сохранить отношение.

Итак, в списке «доступные модули» я хочу показывать только те модули, которые еще не были запущены (т. е. в таблице module_player нет записи) или были запущены, но еще не завершены (т. е. статус 1 в таблице module_player).

Допустим, у меня есть:

таблица игроков

ID Имя
1 Игрок_1
2 Игрок_2

таблица модулей

ID Имя
1 Module_1
2 Модуль_2
3 Module_3

таблица module_player

module_id идентификатор игрока Статус
1 1 1
2 1 2

«Список доступных» должен возвращать только модуль 1 (запущен, но не завершен) и модуль 3 (не запущен).

Проблема в том, что я получаю только модуль 1 в «списке доступных», в этом запросе, который у меня есть, очевидно, что-то не так:

         $modules = DB::table('modules')
            ->join('module_player', 'modules.id', '=', 'module_player.module_id', 'left')
            ->join('players', 'module_player.player_id', '=', 'players.id', 'left')
            ->where('module_player.status', '!=', 2)
            ->where('module_player.player_id, '=', 1)
            ->select('modules.id', 'modules.name', 'modules.image', 'modules.mandatory')->paginate(10);
 

Спасибо

Комментарии:

1. почему вы не используете отношения?

2. хм, потому что я хочу получить не только модули, которые имеют отношение к плееру, но и модули, которые еще не имеют отношения, и разбитые на страницы

3. таким образом, вам нужны модули, статус которых равен 1 в module_player и которые не существуют в этой таблице. как 1 и 3?

4. да, это именно то, что мне нужно

Ответ №1:

Как вы используете

 `where('module_player.player_id, '=', 1)`
 

Вы получите только записи, в которых идентификатор игрока равен 1, поэтому попробуйте выполнить следующий запрос с whereNotIn

 $modules = DB::table('modules')
                ->join('module_player', 'modules.id', '=', 'module_player.module_id', 'left')
                ->join('players', 'module_player.player_id', '=', 'players.id', 'left')
                ->whereNotIn('modules.id', function($query){
                     $query->select('module_id')
                     ->from('module_player')
                     ->where('status', 2);
                })
                ->select('modules.id', 'modules.name', 'modules.image', 'modules.mandatory')->paginate(10);
 

если вы хотите, чтобы это было для какого-либо конкретного пользователя, вы можете передать идентификатор игрока в use()

 ->whereNotIn('modules.id', function($query) use($player_id){
                         $query->select('module_id')
                         ->from('module_player')
                         ->from('player_id', $player_id)
                         ->where('status', 2);
                    })
 

Комментарии:

1. Спасибо, первый работает. Хотелось бы немного объяснить, почему, потому что я думал, что независимо от того, какой пользователь вошел в систему, результаты будут одинаковыми (вот почему я использовал строку, которую вы удалили). Но я попытался запустить/завершить разные модули с двумя разными пользователями, и результаты в порядке. Я не вижу в запросе , как он мог узнать, что, например, у игрока 1 завершен модуль 1 и запущено 2, а у игрока 2 запущен модуль 1 и больше ничего, если я нигде не использую идентификатор игрока, вошедшего в систему. Спасибо!

Ответ №2:

удалите ->where('module_player.player_id, '=', 1) строку, так как она вернет записи с идентификатором игрока 1. и попробуйте выполнить следующий запрос

 $modules = DB::table('modules')
            ->join('module_player', 'modules.id', '=', 'module_player.module_id', 'left')
            ->join('players', 'module_player.player_id', '=', 'players.id', 'left')
            ->where('module_player.status', '!=', 2)
            ->select('modules.id', 'modules.name', 'modules.image', 'modules.mandatory')->paginate(10);