overblog / dataloader-bundle

This bundle allows to easy use DataLoaderPHP in your Symfony project through configuration.
MIT License
46 stars 14 forks source link

More elaborate example #8

Closed rpander93 closed 7 years ago

rpander93 commented 7 years ago

Hi,

I'm a bit in the dark on how to implement this in my schema. I'm using the GraphQLBundle. In my schema, I have a query field named "contracts" which returns an array of contract entities. Each contract contains a one-to-many to an entity named "location". How would I implement this?

In my ContractsResolver class, I'd like to return a promise from my ContractLoader which returns an array of contracts. Then, the locations field on each contract should also return a promise resolving to an array of locations. How should I implement this in my resolvers and scheme?

mcg-web commented 7 years ago

Hi,

You should chaine promise to do that. But it a bit hard to give an example without the schema. Can you please provide the graphql schema? (no need resolver)

rpander93 commented 7 years ago

Hi, thanks for your reply. I created a gist here.

rpander93 commented 7 years ago

I'll make some docs for it btw if I can implement it :)

mcg-web commented 7 years ago

ok i understand, so you need to use a loader for contacts and a other for locations. Locations on contracts should have a independent resolver that returns also a promise. Locations promise will be complete after completing contacts... Let's say you have this: It's a way hard to explain but this could maybe helps...

rpander93 commented 7 years ago

Hi,

I did some puzzling and came up with the following. In my resolver I call the DataLoader for contracts. Is this how you would implement this?

One more question. How can I use the field selelection to conditionally include/exclude locations if the field is not queried? How can I pass any data to the DataLoader to indicate that it can skip the locations?

mcg-web commented 7 years ago

Hi, the best way to that is to add an independent resolver on Contract.locations field that returns the promise of locations (in argument it takes the ids of locations), so it will trigger resolution only if field is selected... no need to attach loaders between them.

rpander93 commented 7 years ago

Right, that idea also came to me this morning. works like a charm! This does present me with another question though. It necessitates me to fetch the ids of related locations even if the field is not resolved fully thereafter. See gist. So I think it would be useful to provide some context to calls to load() or loadMany() to optimize these situations.

mcg-web commented 7 years ago

See this issue to get how to retrieve the list of selected fields, you can execute fetcher only on demand...

rpander93 commented 7 years ago

Hi, thanks for the response. I know of the ResolveInfo object and access to the field selection. Let me try to explain it more carefully.

So, for example, I can see from the field selection that the locations field is not selected. In my ContractLoader however, I still call findContractLocationsForContract to fetch the related location ids. I dont want this when the locations field is not required. How do I pass this information from my ContractResolver to ContractLoader? I tried wrapping the call to findContractLocationsForContract in a closure to delay execution but that didn't work out (since I first thought that is what you hinted at with the related issue).

I created a new gist here.

Btw, thanks for your help so far!!

mcg-web commented 7 years ago

The solution is to not fetch Locations id in your ContractLoader directly but chain it with your promise.

$includeLocations = array_key_exists('locations', $resolveInfo->getFieldSelection());

return $this->contractLoader->loadMany($builder->execute()->fetchAll(\PDO::FETCH_COLUMN))
  ->then(function ($contracts) use ($includeLocations) {
    // here you fetch your locations ids if needed and add them to contracts;
    return $contracts;
  });
rpander93 commented 7 years ago

Ah, interesting! Thanks! That was the hint I needed.