mrichar1 / jsonapi-vuex

Use a JSONAPI api with a Vuex store, with data restructuring/normalization.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to fetch relationships of an item and update the store?

pharkasbence opened this issue · comments

If I use the getRelated action, it fetches the relationships but it doesn't update the relationships of the base item in the store. Is there a way to do that without fetch the base item with includes?

At the moment updates to items in the store only happen when the item itself changes - so references are put in pointing from an item to it's related items, but the related items are not updated to reflect the relationship in the opposite direction. It's actually a hard problem to solve, and is discussed in #87 .

There are still some options though. getRelated currently looks to see if the item it's being passed as an argument is in the store, and has a _jv/relationships section. If it does, then this is used to look up the related items - if not then the item is fetched and then relationships followed.

This means that you can clear the item (or it's relationships) out of the store before calling getRelated, and it will be fetched 'fresh' before fetching the related items. The easiest option is probably just to use the deleteRecord mutation just before the dispatch.

It would actually be trivial for me to add a config option that makes getRelated always refresh the item. Let me know if the above solves your issue, and if it does I'll add this as a new feature.

If I clear the item out of the store before calling getRelated, then it doesn't fetch the item itself, it fetches only its relationships. I use it this way:

this.$store.commit('jv/deleteRecord', { _jv: { type: 'my-type', id: <id> } })
this.$store.dispatch('jv/getRelated', 'my-type/<id>/<relationshipName>')

Apologies - I was talking nonsense before, as I was going from memory, not from the actual code!

It appears that the code never looks at the store at all. It just looks at the argument to getRelated - if it's an object containing _jv/relationships it uses that 'direct' - otherwise it fetches the object from the store and then follows it's relationships.

Since the get from the store uses the get action internally, this should also update the store as it goes... so I'm confused what's happening.

Can you confirm that for you, doing:

this.$store.dispatch('jv/getRelated', 'my-type/<id>')

Doesn't create/update the item in the store? If so then this is a bug that needs investigating.

It does create/update the item in the store, but this doesn't:

this.$store.dispatch('jv/getRelated', 'my-type/<id>/<relationshipName>')

Ahh, I see why this happens - the code is actually leveraging some of the JSONAPI standard, which I had forgotten about, using the related link: https://jsonapi.org/format/#fetching

If you pass the argument my-type/<id>/<relationshipName> then actually the api GET fetches the related item directly, e.g. /my-type/1/myRelationship so yes the original item won't be fetched.

So as it stands there are several pathways through this code, depending on the argument:

  1. my-type/1 - fetches the original item, checks rels, then fetches them.
  2. _jv: { type: 'my-type', id: '1' } - As above
  3. my-type/1/relName - fetches relName directly - doesn't fetch original item.
  4. _jv: { type: 'my-type', id: '1', relationships: { relName {...}}} - As above (different codepath but same effect).

The end result is that the code assumes that if you are specifying the relationship(s) you want to fetch, that you trust the store is up-to-date, and you don't need it to fetch the original item before the relationships.

Now, in terms of solving your original question... 😀

I think you have 2 choices:

  1. Stop adding the relationship to the query - that will force the call to get the original object and it's relationships. However there's an inefficiency in that all relationships will be updated, which you might not care about.

  2. Explicitly call get prior to calling getRelated. You don't need to wait for the get to complete so they can be fired off without using await or then. If you do this often it would be easy to create a wrapper that does this for you (it's easy enough to split the arg to get the item endpoint out).

Let me know what you think! I'm happy enough to make changes to the code - I just can't immediately see a nice way to change things here...

Thank you for your answer, I'd prefer the first choice :)

Great - if you're ok with this solution then please close this call 😃