Почему мой запрос Laravel Eloquent возвращает записи с неправильными датами после применения соединения с дополнительными фильтрами, несмотря на начальные ограничения по дате?

Я работаю с Laravel Eloquent, чтобы получать записи из таблицы views, фильтруя их по определенным датам video_ids и created_at. Мне также нужно присоединиться к другой таблице devices, чтобы применить дополнительные фильтры на основе типов устройств, регионов и имен хостов.

Вот структура моих таблиц:

views таблица:

  • идентификатор (первичный ключ)
  • video_id
  • device_id (внешний ключ)
  • созданный_at (строка даты и времени)

devices таблица:

  • идентификатор (первичный ключ)
  • типы
  • страна
  • имя хоста
  • созданный_at (строка даты и времени)

И вот структура моего запроса:

// this is an example of what $dates looks like
// $dates = ['2024-08-01', '2024-08-02', '2024-08-03'];

public function getViews($videoIds, $dates, $devices, $regions, $sources){
    // Filter by video_id and created_at first
    $query = View::whereIn('views.video_id', $videoIds)
        ->whereIn(DB::raw('DATE(views.created_at)'), $dates);

    // Apply the join and other filters only if devices, regions, or sources are provided
    if (!empty($devices) || !empty($regions) || !empty($sources)) {
        $query->join('devices', 'devices.id', '=', 'views.device_id');

        $query->when(!empty($devices), function ($query) use ($devices) {
            return $query->whereIn('devices.types', array_map(fn($device) => $device['value'], $devices));
        });

        $query->when(!empty($regions), function ($query) use ($regions) {
            return $query->whereIn('devices.country', array_map(fn($region) => $region['value'], $regions));
        });

        $query->when(!empty($sources), function ($query) use ($sources) {
            return $query->whereIn('new_devices.hostname', array_map(fn($source) => $source['value'], $sources));
        });
    }

    return $query->get();
}

Проблема, с которой я столкнулся, заключается в том, что после применения соединения набор результатов включает представления с датами create_at, которых нет в предоставленном мной массиве $dates. Мне нужно, чтобы запрос возвращал только записи, дата которых находится в указанном диапазоне, даже после применения соединения.

Мой вопрос:

  • Почему соединение приводит к тому, что запрос возвращает записи с датами create_at, выходящими за пределы указанного диапазона?
  • Как я могу изменить свой запрос, чтобы он по-прежнему учитывал фильтр даты после применения соединения?

Я ищу решение, которое гарантирует, что набор результатов включает только представления с датами create_at в предоставленном массиве $dates, даже если через соединение применяются дополнительные фильтры.

Любая помощь будет принята с благодарностью!

🤔 А знаете ли вы, что...
PHP можно использовать для работы с XML-документами.


2
50
1

Ответ:

Решено

При выполнении join запросов всегда SELECT используйте нужные вам столбцы. Предположим, у вас есть следующие данные (пропуская ненужные столбцы)

просмотры

идентификатор идентификатор_устройства создано_at 1 1 2024-01-01T00:00:00

устройства

идентификатор создано_at 1 2024-08-02T00:00:00

Если вы выполняете запрос типа

SELECT * from views join devices on devices.id = views.device_id;

Сервер базы данных вернет строку типа

идентификатор идентификатор_устройства создано_at идентификатор создано_at 1 1 2024-01-01T00:00:00 1 2024-08-02T00:00:00

Обратите внимание, что здесь есть два created_at: views.created_at и devices.created_at.

Когда это читается PHP, он преобразует его в объект/массив, и они не могут иметь повторяющихся ключей, он использует только последний дублирующийся ключ. Таким образом, он будет использовать второй массив среди следующих

['id' => 1, 'device_id' => 1, 'created_at' => '2024-01-01T00:00:00'] // This is what you expected
['id' => 1, 'device_id' => 1, 'created_at' => '2024-08-02T00:00:00'] // This is what you got

Скорее всего, это тот сценарий, с которым вы столкнулись: created_at, который вы видите в своих результатах, скорее всего, из другой таблицы.

Чтобы решить эту проблему, вы можете указать, из какой таблицы вы хотите выбрать столбцы, используя функцию выбора.

View::select('views.*', 'devices.created_at as device_created_at', /* other columns you need from devices */)