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();

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