Laravel: фильтровать вложенные отношения?

#laravel #relationship

#laravel #отношения

Вопрос:

Я не понимаю, почему это не работает:

 Device::with(['travel.move.position' => function($q) use ($start, $end) {
    return $q->whereBetween('created_date', [$start, $end]);
}]);
  

Я думаю, что это должно дать мне все устройства с travel->move->position отношениями, но только те, которые position.datetime находятся между $start и $end

Я пробовал что-то вроде

 Device::whereHas('travel.move.position', function($q) use ($start, $end) {
    return $q->whereBetween('created_date', [$start, $end]);
});
  

и я получаю ожидаемый результат, но я не получаю нетерпеливых отношений.

ДОБАВЛЕН ЗАПРОС ДЛЯ ОТВЕТА 1:

 select * 
from `devices` 
where exists (
    select * from `travels` 
    inner join `travel_info` on `travel_info`.`id` = `travels`.`info_id` 
    where `devices`.`id` = `travel_info`.`device_id` 
    and exists (
        select * 
        from `moves` 
        where `moves`.`travel_id` = `travels`.`id` 
        and exists (
            select * from `positions` 
            where `moves`.`position_id` = `positions`.`id` and `created_at`
            between ? and ?
        )
    ) 
  

Запрос идеален (как и whereHas), но когда выполняется with():

 select `travels`.*, `travel_info`.`device_id` 
from `travels` 
inner join `travel_info` on `travel_info`.`id` = `travels`.`info_id` 
where `travel_info`.`device_id` in (?, ?, ?) 

.... 
select * from `moves` where `moves`.`travel_id` in (?, ..., ?)
....
select * from `positions` where `positions`.`id` in (?, ..., ?)
  

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

1. Этот код только фильтрует position , а не device . Таким образом, вы все равно получите все устройства, но внутри каждого отношения устройство-> перемещение-> переместить вы увидите только позиции, соответствующие вашему фильтру.

2. Ваше поле действительно называется datetime ? Поскольку это зарезервированное слово в mysql: dev.mysql.com/doc/refman/5.7/en/keywords.html Я думаю, что вам может понадобиться указать это whereBetween(' datetime ', [$start, $end]);

3. попробуйте добавить with('travel.move.position') ко второму запросу. Затем он дает вам ожидаемый результат плюс загруженные отношения.

4. @Cyclone . Нет, это испанское слово, которое означает то же самое, но я изменил для лучшего понимания.

5. @kanashin — хаха, хорошо, тогда я понимаю — возможно, вам следует изменить это на что-то другое, например created_time и т.д. =)

Ответ №1:

Хорошо, после долгого обсуждения с автором вопроса мы обнаружили, что проблема заключается не в самом запросе, а скорее в неправильной загрузке отношений в with() инструкции.

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

 $start = '2016-10-01';
$end = '2016-10-31';

$devices = Device::whereHas('travel.move.position', function($q) use ($start, $end) {
    $q->whereBetween('created_at', [$start, $end]);
})->with([
    'travel' => function($q1) use ($start, $end) {
        $q1->whereHas('move.position', function($q2) use ($start, $end) {
            $q2->whereBetween('created_at', [$start, $end]);
        });
    },
    'travel.move' => function($q1) use ($start, $end) {
        $q1->whereHas('position', function($q2) use ($start, $end) {
            $q2->whereBetween('created_at', [$start, $end]);
        });
    },
    'travel.move.position' => function($q1) use ($start, $end) {
        $q1->whereBetween('created_at', [$start, $end]);
    }
])->get();
  

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

1. Спасибо, но это то же самое: я получаю device-> travel-> move-> position-> created_at == ‘2016-09-23’

2. Не могли бы вы включить ведение журнала запросов к базе данных и проверить приведенный выше код в SQL-запросе и опубликовать его в своем вопросе?

3. Может ли один ход иметь несколько позиций?

4. Нет, у него не может быть больше одного. Это отношение принадлежит

5. В добавленных вами SQL-запросах есть две подозрительные вещи: 1. where moves.position_id = positions.id and created_at ... почему created_at не имеет префикса к имени таблицы, например, positions.created_at ? 2. select * from positions where positions.id in (?, ..., ?) неверно. Это должно быть ... positions.move_id in (?, ..., ?)