Background first on my question. A user hasMany contacts
and a contact
hasMany anniversaries
. I want to filter the upcoming anniversaries. So far I have this:
$filtered = auth()->user()->contacts()->get()->each(function ($contact) {
$contact->anniversaries->filter(function ($anniversary) {
// return true or false based on a method on the anniversary model
return $anniversary->method() == true;
});
});
But this just returns all the contacts (obviously) with all their anniversaries, and I wish to exclude the ones that are false when calling the $anniversary->method()
.
Whatever is in the $anniversary->method()
is not important, this just returns a true
or false
.
When I do the following, it works:
$collection = auth()->user()->anniversaries()->get();
$filtered = $collection->filter(function ($anniversary) {
return $anniversary->method() == true;
});
I get only the anniversaries from where the $anniversary->method()
is indeed true
.
My question is mainly, why does this happen, I only want to understand it, not so much need an answer on how to make it work. Thanks in advance for any insights!
CodePudding user response:
In the first example, you are only filtering the anniversaries
of each contact. You are not filtering the contacts
directly per see.
$filtered = auth()->user()->contacts()->get()->each(function ($contact) {
// You are filtering only the anniversaries of each contact
$anniversaries = $contact->anniversaries->filter(function ($anniversary) {
return $anniversary->method() == true;
});
// over here you should get the same as in your second example
dd($anniversaries);
});
In your second example you are doing the following pseudo-code:
- Fetch all anniversaries of each user
- Filter the anniversaries that matches
$anniversary->method() === true
To get the same results in the first example you would have to use a combination of filter
with ->count()
$filtered = auth()->user()->contacts()->get()->filter(function ($contact) {
$filteredByMethod = $contact->anniversaries->filter(function ($anniversary) {
// return true or false based on a method on the anniversary model
return $anniversary->method() == true;
});
return $filteredByMethod->count() > 0;
});
Which one is more performant is beyond the scope of this answer.
Quick tip
The method filter
from Laravel's collections
needs to return a truthy
value to be filtered by. Since your method
returns true or false
you can just call the method directly without comparing with true
:
$collection = auth()->user()->anniversaries()->get();
$filtered = $collection->filter(function ($anniversary) {
return $anniversary->method();
});