I have two tables that has One to Many relationship.
Bookings - (id)
booking_tasks - (id, booking_id,user_id)
one booking h
This requires a more complex query:
$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();
I found the answer from Jonas worked but because it was a query with a join, it caused issues with eager loading relations from the model. So I used that solution with the whereHas
function to come up with a solution that could be used in scopes and so would be reusable.
The first step involves adding a macro to the query Builder
in the 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);
});
});
This basically makes the sub-query part of Jonas's answer more generic, allowing you to specify the join table and the column they join on. It assumes the other column is an 'id' column, so it can be improved further. To use this you'd then be able to do:
$userId = 2;
Booking::whereHas('tasks', function ($tasks) use ($userId) {
$tasks->where('user_id', $userId)
->whereLatestRelation((new Task)->getTable(), 'booking_id');
});
It's the same logic as the accepted answer, but a bit easier to re-use. It will, however, be a little slower, but that should be worth the re-usability.
This
I want to get list of bookings that, user_id = 2 for latest booking_task for the bookings. I do not want to check other old booking_tasks of the bookings.
doesn't make an awful lot of sense, but what I think you want is to get out is all the bookings with user_id == 2, where has at least 1 task and with only the lastest task for each booking? So perhaps this will work?
Booking::where('user_id', 2)->with('tasks', function($query){
$query->orderBy('created_at', 'DESC')->take(1);
})->has('tasks')->get();
Have you tried ->first() combined with reversed orderBy()?
Like $q->where(‘user_id’,’=‘,’2’)->orderBy(‘id’,’DESC’)->first();