cloudcreativity / laravel-json-api

JSON API (jsonapi.org) package for Laravel applications.
http://laravel-json-api.readthedocs.io/en/latest/
Apache License 2.0
780 stars 109 forks source link

Filter relations #353

Closed deividaspetraitis closed 5 years ago

deividaspetraitis commented 5 years ago

Hello,

For example I have posts resource that has many comments. My goal is to load only coments that has id of the following 1 and 2, how I can do that using this package?

This question might look a bit dumb but by placing rules in adapters filter method is not working and I suspect it is because about package internals and how it loads relations and need some ideas.

There are two cases Querying Relationship Existence and Constraining Eager Loads I'm interested in.

Querying Relationship Existence

// Retrieve posts with at least one comment containing words like foo%...
$posts = App\Post::whereHas('comments', function ($query) {
    $query->where('content', 'like', 'foo%');
})->get();

This one should be placed in filter method in Adapter I suspect?

Constraining Eager Loads

This one I"m stuck with. For example:

$users = App\Post::with(['comments' => function ($query) {
    $query->where('title', 'like', '%first%');
}])->get();

Where I should place this code using this package?

Thanks

lindyhopchris commented 5 years ago

The package already supports:

GET /api/comments?id[]=1&id[]=2

Or:

GET /api/posts/123/comments?id[]=1&id[]=2

Yes the querying relationship existence should be placed in the filter method.

For the constraining eager loads, you'll need to overload this method on your adapter: https://github.com/cloudcreativity/laravel-json-api/blob/develop/src/Eloquent/Concerns/IncludesModels.php#L96-L101

deividaspetraitis commented 5 years ago

Wow, it's a good tip!

Few things more to clear out regarding constraining eager loads.

What about if I want to specify query in parent resource ( because need to retrieve it as well ), request would look like: GET /posts/1?filter[comments.id][]=1&filter[comments.id][]=2?

So in such case I need to provide filter ( as in example ) and overload mentioned method?

Thanks for your time!

lindyhopchris commented 5 years ago

So my general tip with these things is write the Eloquent query first and get that working outside of this package first... then it's just a matter of dropping that into the filter method.

For the thing you've asked about, it would be something like this (I haven't tried, so you'll need to check if I've got any typos!)

$query->whereHas('comments', function ($q) use ($filter) {
   $q->whereKey($filter['comments.id']);
});
deividaspetraitis commented 5 years ago

With whereHas is pretty much clear where to place it.

Let's say I have the following query: GET /posts/1?filter[comments.id]=1,2

My goal is even if post doesn't have comments with ID 1,2 I want posts resource to be returned. It's something that SPEC it self allows, correct me if I'm wrong.

The problem with whereHas is that in such scenario it will not work out ( I will receive 404 ) and that's the reason why I need to use with instead.

The question is where I can put code block with with logic and apply filters from filter. For example where I should place such block:

if ($comments = $filters->get('comments.id')) {
   $query->with(['comments' => function($q) {
        $q->wherein($comments);
   }]);
}

I'm sorry, I think I have confused you and hope this comment will make things explained.

lindyhopchris commented 5 years ago

That code block can go in the filter method, because the filter method is invoked if there are any filters!

Whether it works for what you're trying to do, I have no idea. If it doesn't, give me the complete Eloquent query that you would run to replicate what you're trying to do outside of this package. Then we can work out where to put it in the adapter to replicate it. However if you're not able to write an Eloquent query for what you're trying to do outside of this package, it will never work within this package!

deividaspetraitis commented 5 years ago

That code block can go in the filter method, because the filter method is invoked if there are any filters!

I think this is the answer to my question, thanks.