Description
Thank you for this wonderful library.
Until the change introduced by #702 by @sudevva I was able to "share" instances of Types in my prefetch logic.
The change to this line:
means that the source is now purged from results. If I then want to return the same Type instance later, I will get an error:
UnexpectedValueException: Object not found
/workspaces/graphqlite/src/PrefetchBuffer.php:90
This error only shows up in a nested prefetch context, specifically where an intermediate type is being prefetched, instances of that type are being shared, and it has a prefetched child field also.
In the test suite on this repo, this demonstrates the issue:
// tests/Integration/EndToEndTest.php
// A NEW TEST TO DEMONSTRATE THE ISSUE
public function testPrefetchingDeeplyNested(): void
{
$schema = $this->mainContainer->get(Schema::class);
assert($schema instanceof Schema);
$schema->assertValid();
$queryString = '
query {
companies {
contact {
name
supervisor {
name
}
}
}
}
';
$result = GraphQL::executeQuery(
$schema,
$queryString,
null,
new Context(),
);
$this->assertSame([
'companies' => [
[
'contact' => [
'name' => 'Kate',
'supervisor' => [
'name' => 'The Boss',
],
],
],
[
'contact' => [
'name' => 'Kate',
'supervisor' => [
'name' => 'The Boss',
],
],
],
]
], $this->getSuccessResult($result));
}
// tests/Fixtures/Integration/Controllers/CompanyController
/**
* @return Company[]
*/
#[Query]
public function getCompanies(): array
{
return [new Company('Company1'), new Company('Company2')];
}
// tests/Fixtures/Integration/Types/CompanyType.php
/**
* @param Company[] $companies
*
* @return Contact[]
*/
public static function prefetchContacts(array $companies): array
{
$contacts = [];
$kate = new Contact('Kate');
foreach ($companies as $company) {
// This works
//$contacts[$company->name] = new Contact('Kate');
// This does not work
$contacts[$company->name] = $kate;
}
return $contacts;
}
// tests/Fixtures/Integration/Models/Contact.php
/** @param Contact[] $contacts */
#[Field]
public function getSupervisor(#[Prefetch('prefetchSupervisors')] array $supervisors): Contact|null
{
return $supervisors[$this->name] ?? null;
}
/**
* @param Contact[] $contacts
*
* @return Contact[]
*/
public static function prefetchSupervisors(array $contacts): array
{
$supervisors = [];
$theBoss = new Contact('The Boss');
foreach ($contacts as $contacts) {
$supervisors[$contacts->name] = $theBoss;
}
return $supervisors;
}
Notably, it is the sharing of the $kate
Type instance in the prefetchContacts method on CompanyType.php that causes the error:
There was 1 error:
1) TheCodingMachine\GraphQLite\Integration\EndToEndTest::testPrefetchingDeeplyNested
UnexpectedValueException: Object not found
/workspaces/graphqlite/src/PrefetchBuffer.php:90
/workspaces/graphqlite/src/Parameters/PrefetchDataParameter.php:60
/workspaces/graphqlite/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromise.php:63
/workspaces/graphqlite/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromise.php:50
/workspaces/graphqlite/vendor/webonyx/graphql-php/src/Executor/Promise/Adapter/SyncPromiseAdapter.php:147
/workspaces/graphqlite/vendor/webonyx/graphql-php/src/GraphQL.php:109
/workspaces/graphqlite/tests/Integration/EndToEndTest.php:2477
If I revert to using a new Type instance then the error goes away (see comment in above code). But this means I can't cache/reuse my Type instances in deeply nested prefetch scenarios.
Note if I don't fetch the child field supervisor
(itself prefetched) then the error goes away. It is only apparent with nested prefetches.
Commenting out this line appears to fix the issue, but I'm unsure if this has repercussions elsewhere:
Is this expected behaviour? Am I meant to always return a new Type instance for each result, even if the result is identical and used elsewhere? This seems like a change if so?