Laravel - где проверять последние записи отношений без проверки других
У меня есть две таблицы, которые имеют отношение один ко многим.
Бронирование - (id)
booking_tasks - (id, booking_id,user_id)
одно бронирование имеет много задач одно задание имеет одно бронирование
Модель бронирования:
public function tasks() {
return $this->hasMany('App\BookingTask');
}
BookingTask Модель
public function booking() {
return $this->belongsTo('App\Booking');
}
Я хочу получить список заказов, user_id = 2 для последних booking_task для заказов. Я не хочу проверять другие старые booking_tasks заказов.
например:
Я хочу проверить, есть ли последняя запись user_id = 2 для booking_task, а затем получить его в качестве бронирования. В примере последний booking_task's user_id = 5. Таким образом, он не будет получен в качестве бронирования.
Мой код:
$bookings=Booking::whereHas('tasks',function($q){
$q->where('user_id',2);//this will check whether any of record has user_id =2
})->get();
Я также использовал это: но это не правильно,
$bookings=Booking::whereHas('tasks',function($q){
$q->latest()->where('user_id',2)->limit(1);//this will check whether any of record has user_id =2 and return latest one.
})->get();
Как я могу решить эту проблему, я должен использовать Laravel Eloquent также.
3 ответа
Это требует более сложного запроса:
$bookings = Booking::select('bookings.*')
->join('booking_tasks', 'bookings.id', 'booking_tasks.booking_id')
->where('booking_tasks.user_id', 2)
->where('booking_tasks.id', function($query) {
$query->select('id')
->from('booking_tasks')
->whereColumn('booking_id', 'bookings.id')
->latest()
->limit(1);
})->get();
Я обнаружил, что ответ от Jonas сработал, но поскольку это был запрос с объединением, это вызвало проблемы со стремительной загрузкой отношений из модели. Так что я использовал это решение с whereHas
функция, чтобы придумать решение, которое может быть использовано в областях и, следовательно, будет многоразовым.
Первый шаг включает добавление макроса в запрос Builder
в AppServiceProvider
,
use Illuminate\Database\Query\Builder;
Builder::macro('whereLatestRelation', function ($table, $parentRelatedColumn)
{
return $this->where($table . '.id', function ($sub) use ($table, $parentRelatedColumn) {
$sub->select('id')
->from($table . ' AS other')
->whereColumn('other.' . $parentRelatedColumn, $table . '.' . $parentRelatedColumn)
->latest()
->take(1);
});
});
Это в основном делает часть подзапроса ответа Jonas более общей, позволяя вам указать таблицу соединений и столбец, к которому они присоединяются. Предполагается, что другой столбец является столбцом id, поэтому его можно улучшить еще больше. Чтобы использовать это, вы сможете:
$userId = 2;
Booking::whereHas('tasks', function ($tasks) use ($userId) {
$tasks->where('user_id', $userId)
->whereLatestRelation((new Task)->getTable(), 'booking_id');
});
Это та же логика, что и принятого ответа, но немного проще для повторного использования. Это будет, однако, немного медленнее, но это должно стоить повторного использования.
Это
Я хочу получить список заказов, user_id = 2 для последних booking_task для заказов. Я не хочу проверять другие старые booking_tasks заказов.
не имеет особого смысла, но я думаю, что вы хотите получить все заказы с user_id == 2, где есть хотя бы 1 задание и только самое последнее задание для каждого бронирования? Так что, возможно, это будет работать?
Booking::where('user_id', 2)->with('tasks', function($query){
$query->orderBy('created_at', 'DESC')->take(1);
})->has('tasks')->get();
Вы пробовали ->first() в сочетании с обратным orderBy()?
Например, $q->where('user_id', '=', '2')->orderBy('id', 'DESC')->first();