How I can filter included resources by their fields
sunnydesign opened this issue · 8 comments
Hi!
I want to filter included resources by their fields.
I tried to use withFilters()
for the relationship fields, but it didn’t work:
For example, for this endpoints I want to filter related cart-items
by their field practiceLocationId
and to include them only:
http://localhost/api/v1/distributors?include=cart-items&filters[practiceLocationId]=89
My schema:
class DistributorSchema extends BaseSchema
{
...
public function fields(): array
{
...
HasMany::make('cart-items')->withFilters(
Where::make('practiceLocationId')
)
...
}
...
}
Big thanks for your answer.
Hi! I think you've created this issue in the wrong repo - from the code example I think you're using laravel-json-api/laravel
?
In answer to your question though, there is nothing in the JSON:API spec that allows you to filter included resources. So this isn't supported. Filters apply to the primary set of resources, that appears in the data
member of the document.
It's so bad, my friend. I'm very disapointed. How can I solve this problem? Maybe you tell me a solution?
You right, I use laravel-json-api/laravel
. But I always write here :)
So you probably need to explain your use case as there's most likely a different way to do this. For example, you can filter a relationship endpoint, so you could do:
GET /api/v1/distributors/123/cart-items?filter[practiceLocationId]=456
So, my goal is to receive in one request all distributors
with their cart-items
which deep related to distributors
through product-distributors
. Related cart-items
must be filtered by practiceLocationId
field, which belong cart-items
resource. And distributors
must be filtered by cart-items.practiceLocationId
to.
This is what I want to get with Eloquent:
$distributors = \App\Models\Distributor::whereHas('cartItems', function(Illuminate\Database\Eloquent\Builder $query) {
$query->where('cart_item.practice_location_id', '=', 1);
});
foreach($distributors->get() as $distributor) {
echo '<strong>distributor.id: ' . $distributor->id . '</strong>';
echo '<br />';
foreach ($distributor->cartItems()->where('cart_item.practice_location_id', '=', 1)->get() as $cartItem) {
echo 'cartItem.id: ' . $cartItem->id . ' cartItem.practice_location_id: ' . $cartItem->practice_location_id;
echo '<br />';
}
echo '<br />';
}
And result:
distributor.id: 30
cartItem.id: 222 cartItem.practice_location_id: 1
cartItem.id: 1 cartItem.practice_location_id: 1
distributor.id: 33
cartItem.id: 255 cartItem.practice_location_id: 1
distributor.id: 27
cartItem.id: 361 cartItem.practice_location_id: 1
distributor.id: 23
cartItem.id: 484 cartItem.practice_location_id: 1
So it's unrealistic to do that in one HTTP request using the JSON:API spec. Your example code of doing it with Eloquent has 1 database query to get the distributors, then an additional database query for each distributor to get the required cart items.
You've got an N+1 problem there, as the more distributors you have, the more database queries you'd get.
There's nothing in the JSON:API spec that allows you to do that at the moment in a single request. So you'd either need to accept multiple HTTP requests (which is along the lines of the N+1 problem you've got in the above example). Or you'd need to package the data up yourself in your own controller action. You can add custom actions, they are documented here:
https://laraveljsonapi.io/docs/1.0/routing/custom-actions.html
Yes, i know about N+1, my code was for example. Anyway, my problem has already been solved by another way.
Big thanks for your answers.