Skip to content

Control count of included resources when querying the relation of an endpoint #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jstoone opened this issue Feb 17, 2017 · 4 comments

Comments

@jstoone
Copy link
Contributor

jstoone commented Feb 17, 2017

Story time

When requesting the relation of an endpoint i.e: /api/v1/companies/1/users i get a list of users within the company of ID 1, true.
Now let's say I want to include some "likes" for each user, so we add that as a query parameter: /app/v1/companies/1/users?include=stars. Now we get a list of users, and all their stars have been included in the response as well, true.

Problem time

Let's say I want to be more specific, and only get 5 stars per user included. How would I go around solving this?

  • If there are 3 users we should get 5 stars for each of them, totaling at 15 stars
  • If one of the users only has 2 stars those are still included and the other two users will still have their 5 stars. Giving a total of 12 stars being included.

First try

In the App\Models\User eloquent model of mine i just cold heartedly added a limit onto the query:

public function stars()
{
    return $this->hasMany(Stars::class)->limit(5);
}

But this resulted in there only being returned 5 stars in total.

Where to go next

I am trying to dig deeper how loading of included models works, but I am having a hard time navigating around and finding the source where the final query is constructed.
It does seem a bit naughty to me that the stars relationship shares its limit globally, since it should only limit the amount of stars for that one user instance, right? I would understand if the relation method was static and therefore would apply to the entire collection of users.

Am I missing something, a filter or something that actually lets me do exactly this? Looking forward to hearing from you @lindyhopchris :)

@lindyhopchris
Copy link
Member

Hi @jstoone

What you are trying to do isn't supported by the neomerx/json-api package that does the encoding. The include param says include all stars that are referenced in the relationships of the main data key in the document. So if you've got 10 users in that data key, then every unique star relationship identifier that is referenced in the stars relationship of each user will be included in the include key.

To be fair to that encoding package, what you are trying to do isn't mentioned in the JSON API spec (as far as I can see). Under the section on the include param there is no mention of a server supporting limiting the number of included resources by type.

My suggestion (though I'm obviously not familiar with your use case and client/server set up) would be to user the following:

Add a top-stars relationship to the user that only returns 5 stars. Then you can do:

/companies/1/users?include=top-stars

On your users model, you can then define a has-many relationship called topStars() that only returns the five most recent. I.e.:

public function stars()
{
    return $this->hasMany(Star::class);
}

public function topStars()
{
    return $this->stars()->orderBy('created_at', 'desc')->limit(5);
}

To be honest, if your users can have lots of stars, it's probably not worth having a JSON API relationship in the users resource that lists all of them because it'd be really inefficient.

What you'd actually do is instead of returning data for the stars relationship in the users schema, return a link instead to. This means if you do ever need to get all the stars for a user, you use /users/1/stars.

To return a link instead:

return [
    'top-stars' => [self::DATA => $resource->topStars],
    'stars' => [self::SHOW_RELATED => true]
];

As per: https://github.com/neomerx/json-api/wiki/Schemas

Hope this makes some sort of sense!

@jstoone
Copy link
Contributor Author

jstoone commented Feb 20, 2017

Uff nice reponse, and that was actually what we ended up trying.
I am actually in the process of writing an issue to laravel/framework, since it in my eyes looks like a bug in laravel. I'm going to write the bug report after lunch, so I'll make sure to post a link to the issue when It's done.

I'll close this issue for now since as you mention nothing that is natively supported by the spec, and therefore not relevant to the package itself. 👍

@jstoone jstoone closed this as completed Feb 20, 2017
@lindyhopchris
Copy link
Member

great, yep keep me in the loop with the laravel bug.

@jstoone
Copy link
Contributor Author

jstoone commented Feb 20, 2017

@lindyhopchris as promised: laravel/framework#18014

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants