It is possible to query once and split the collection of results based on the relationship. You can use the mapWithKeys() method to achieve this. The mapWithKeys() method will iterate through the collection and return a new collection with the keys and values that you specify.
In your case, you can use the mapWithKeys() method to iterate through the collection and return a new collection with the users and their posts that are already published and upcoming.
Below is an example of how you can use the mapWithKeys() method to achieve this:
$usersWithPosts = User::withWhereHas(
'posts',
fn ($query) => $query->whereDate('published_at', '<=', now()->addDays(3)->endOfDay())
)->get();
$usersWithPublishedPosts = $usersWithPosts->mapWithKeys(function ($user) {
return [
$user->id => $user->posts->filter(function ($post) {
return $post->published_at <= now();
})
];
});
$usersWithUpcomingPosts = $usersWithPosts->mapWithKeys(function ($user) {
return [
$user->id => $user->posts->filter(function ($post) {
return $post->published_at > now();
})
];
});
The $usersWithPublishedPosts collection will contain the users and their posts that are already published, and the $usersWithUpcomingPosts collection will contain the users and their posts that are upcoming.