Laravel Eager Load With Order and Count
The Laravel Docs have a section Constraining Eager Loads
Which hides a lot of power available to you while loading relationships.
It can be used for more than just “constraints”
If you want to sort your relations by a specific fields, add a count of a nested relationship, or in other ways add to teh query - this is where you can do it.
Sometimes you may wish to eager load a relationship but also specify additional query conditions for the eager loading query. You can accomplish this by passing an array of relationships to the with method where the array key is a relationship name and the array value is a closure that adds additional constraints to the eager loading query:
The API docs for Model/Load just define the parameter as
array|string $relations
This doesn’t tell us much - but the comment above says a bit more
the array key is a relationship name and the array value is a closure that adds additional constraints to the eager loading query
and the example given is a WHERE
clause but we can also us it for other things like sorting and counting
Here I have a poll, each poll asks one question which has several possible answers and users respond to each answer.
Questions have a field sort_order which defines the order they should be shown in.
$poll->load([
'answers' =>
fn ($query) =>
$query
->withCount('responses')
->orderBy('sort_order', 'asc')
]);
This results in a poll object with related answers in the required order with a count of responses for each answer - as below.
"poll": {
"id": 10009,
"question": "What is your favourite colour",
"answers": [
{
"id": 10029,
"poll_id": 10009,
"answer": "Red",
"sort_order": 1,
"responses_count": 3
},
{
"id": 10028,
"poll_id": 10009,
"answer": "Blue",
"sort_order": 2,
"responses_count": 1
}
],
}
Count all
Note that if I want the count of all responses for all polls I can simply use loadCount (and I can do both counts if wanted)
$poll->loadCount('responses');
And in my Poll model I define the relationship via hasManyThrough
public function responses(): HasManyThrough
{
return $this->hasManyThrough(
PollResponse::class,
PollAnswer::class
);
}