Skip to content

Commit 2f517a1

Browse files
author
discodingo
committed
Fix prefetching the same GQL type for batch request
1 parent 11a2600 commit 2f517a1

File tree

10 files changed

+295
-26
lines changed

10 files changed

+295
-26
lines changed

src/PrefetchBuffer.php

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace TheCodingMachine\GraphQLite;
66

7+
use GraphQL\Type\Definition\ResolveInfo;
78
use function array_key_exists;
89
use function md5;
910
use function serialize;
@@ -20,48 +21,70 @@ class PrefetchBuffer
2021
private array $results = [];
2122

2223
/** @param array<int,mixed> $arguments The input arguments passed from GraphQL to the field. */
23-
public function register(object $object, array $arguments): void
24-
{
25-
$this->objects[$this->computeHash($arguments)][] = $object;
24+
public function register(
25+
object $object,
26+
array $arguments,
27+
?ResolveInfo $info = null
28+
): void {
29+
$this->objects[$this->computeHash($arguments, $info)][] = $object;
2630
}
2731

2832
/** @param array<int,mixed> $arguments The input arguments passed from GraphQL to the field. */
29-
private function computeHash(array $arguments): string
30-
{
31-
return md5(serialize($arguments));
33+
private function computeHash(
34+
array $arguments,
35+
?ResolveInfo $info = null
36+
): string {
37+
if (
38+
null === $info
39+
|| null === ($queryBody = $info->operation->loc?->source->body)
40+
) {
41+
return md5(serialize($arguments));
42+
}
43+
return md5(serialize($arguments) . $queryBody);
3244
}
3345

3446
/**
3547
* @param array<int,mixed> $arguments The input arguments passed from GraphQL to the field.
3648
*
3749
* @return array<int, object>
3850
*/
39-
public function getObjectsByArguments(array $arguments): array
40-
{
41-
return $this->objects[$this->computeHash($arguments)] ?? [];
51+
public function getObjectsByArguments(
52+
array $arguments,
53+
?ResolveInfo $info = null
54+
): array {
55+
return $this->objects[$this->computeHash($arguments, $info)] ?? [];
4256
}
4357

4458
/** @param array<int,mixed> $arguments The input arguments passed from GraphQL to the field. */
45-
public function purge(array $arguments): void
46-
{
47-
unset($this->objects[$this->computeHash($arguments)]);
59+
public function purge(
60+
array $arguments,
61+
?ResolveInfo $info = null
62+
): void {
63+
unset($this->objects[$this->computeHash($arguments, $info)]);
4864
}
4965

5066
/** @param array<int,mixed> $arguments The input arguments passed from GraphQL to the field. */
51-
public function storeResult(mixed $result, array $arguments): void
52-
{
53-
$this->results[$this->computeHash($arguments)] = $result;
67+
public function storeResult(
68+
mixed $result,
69+
array $arguments,
70+
?ResolveInfo $info = null
71+
): void {
72+
$this->results[$this->computeHash($arguments, $info)] = $result;
5473
}
5574

5675
/** @param array<int,mixed> $arguments The input arguments passed from GraphQL to the field. */
57-
public function hasResult(array $arguments): bool
58-
{
59-
return array_key_exists($this->computeHash($arguments), $this->results);
76+
public function hasResult(
77+
array $arguments,
78+
?ResolveInfo $info = null
79+
): bool {
80+
return array_key_exists($this->computeHash($arguments, $info), $this->results);
6081
}
6182

6283
/** @param array<int,mixed> $arguments The input arguments passed from GraphQL to the field. */
63-
public function getResult(array $arguments): mixed
64-
{
65-
return $this->results[$this->computeHash($arguments)];
84+
public function getResult(
85+
array $arguments,
86+
?ResolveInfo $info = null
87+
): mixed {
88+
return $this->results[$this->computeHash($arguments, $info)];
6689
}
6790
}

src/QueryField.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,28 +104,28 @@ public function __construct(string $name, OutputType $type, array $arguments, Re
104104

105105
$prefetchBuffer = $context->getPrefetchBuffer($this);
106106

107-
$prefetchBuffer->register($source, $args);
107+
$prefetchBuffer->register($source, $args, $info);
108108

109109
return new Deferred(function () use ($prefetchBuffer, $source, $args, $context, $info, $prefetchArgs, $prefetchMethodName, $arguments, $resolveFn, $originalResolver) {
110-
if (! $prefetchBuffer->hasResult($args)) {
110+
if (! $prefetchBuffer->hasResult($args, $info)) {
111111
if ($originalResolver instanceof SourceResolverInterface) {
112112
$originalResolver->setObject($source);
113113
}
114114

115115
// TODO: originalPrefetchResolver and prefetchResolver needed!!!
116116
$prefetchCallable = [$originalResolver->getObject(), $prefetchMethodName];
117117

118-
$sources = $prefetchBuffer->getObjectsByArguments($args);
118+
$sources = $prefetchBuffer->getObjectsByArguments($args, $info);
119119

120120
assert(is_callable($prefetchCallable));
121121
$toPassPrefetchArgs = $this->paramsToArguments($prefetchArgs, $source, $args, $context, $info, $prefetchCallable);
122122

123123
array_unshift($toPassPrefetchArgs, $sources);
124124
assert(is_callable($prefetchCallable));
125125
$prefetchResult = $prefetchCallable(...$toPassPrefetchArgs);
126-
$prefetchBuffer->storeResult($prefetchResult, $args);
126+
$prefetchBuffer->storeResult($prefetchResult, $args, $info);
127127
} else {
128-
$prefetchResult = $prefetchBuffer->getResult($args);
128+
$prefetchResult = $prefetchBuffer->getResult($args, $info);
129129
}
130130

131131
foreach ($arguments as $argument) {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\GraphQLite\Fixtures\Integration\Controllers;
6+
7+
use TheCodingMachine\GraphQLite\Annotations\Query;
8+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Company;
9+
10+
class CompanyController
11+
{
12+
/** @Query() */
13+
public function getCompany(string $id): Company
14+
{
15+
return new Company('Company');
16+
}
17+
}

tests/Fixtures/Integration/Controllers/ContactController.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ public function getContacts(): array
2626
];
2727
}
2828

29+
/**
30+
* @Query()
31+
*/
32+
public function getContact(string $name): ?Contact
33+
{
34+
return match( $name ) {
35+
'Joe' => new Contact('Joe'),
36+
'Bill' => new Contact('Bill'),
37+
default => null,
38+
};
39+
}
40+
2941
/**
3042
* @Mutation()
3143
* @param Contact $contact
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\GraphQLite\Fixtures\Integration\Models;
6+
7+
use TheCodingMachine\GraphQLite\Annotations\Type;
8+
9+
/**
10+
* @Type()
11+
*/
12+
class Company
13+
{
14+
public function __construct(
15+
public readonly string $name
16+
) {
17+
}
18+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\GraphQLite\Fixtures\Integration\Types;
6+
7+
use TheCodingMachine\GraphQLite\Annotations\ExtendType;
8+
use TheCodingMachine\GraphQLite\Annotations\Field;
9+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Company;
10+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Contact;
11+
12+
/**
13+
* @ExtendType(class=Company::class)
14+
*/
15+
class CompanyType
16+
{
17+
/**
18+
* @Field()
19+
*/
20+
public function getName(Company $company): string
21+
{
22+
return $company->name;
23+
}
24+
25+
/**
26+
* @Field(prefetchMethod="prefetchContacts")
27+
*/
28+
public function getContact(Company $company, array $contacts): ?Contact
29+
{
30+
return $contacts[$company->name] ?? null;
31+
}
32+
33+
public function prefetchContacts(array $companies): array
34+
{
35+
$contacts = [];
36+
37+
foreach ($companies as $company) {
38+
$contacts[$company->name] = new Contact('Kate');
39+
}
40+
41+
return $contacts;
42+
}
43+
}

tests/Fixtures/Integration/Types/ContactType.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace TheCodingMachine\GraphQLite\Fixtures\Integration\Types;
55

6+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Post;
67
use function array_search;
78
use function strtoupper;
89
use TheCodingMachine\GraphQLite\Annotations\ExtendType;
@@ -52,4 +53,52 @@ public function prefetchContacts(iterable $contacts, string $prefix)
5253
'prefix' => $prefix
5354
];
5455
}
56+
57+
/**
58+
* @Field(prefetchMethod="prefetchPosts")
59+
* @return Post[]|null
60+
*/
61+
public function getPosts($contact, $posts): ?array
62+
{
63+
return $posts[$contact->getName()] ?? null;
64+
}
65+
66+
public function prefetchPosts(iterable $contacts): array
67+
{
68+
$posts = [];
69+
foreach ($contacts as $contact) {
70+
$contactPost = array_filter(
71+
$this->getContactPosts(),
72+
fn(Post $post) => $post->author?->getName() === $contact->getName()
73+
);
74+
75+
if ([] === $contactPost) {
76+
continue;
77+
}
78+
79+
$posts[$contact->getName()] = $contactPost;
80+
}
81+
82+
return $posts;
83+
}
84+
85+
private function getContactPosts(): array
86+
{
87+
return [
88+
$this->generatePost('First Joe post', '1', new Contact('Joe')),
89+
$this->generatePost('First Bill post', '2', new Contact('Bill')),
90+
$this->generatePost('First Kate post', '3', new Contact('Kate')),
91+
];
92+
}
93+
94+
private function generatePost(
95+
string $title,
96+
string $id,
97+
Contact $author,
98+
): Post {
99+
$post = new Post($title);
100+
$post->id = $id;
101+
$post->author = $author;
102+
return $post;
103+
}
55104
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TheCodingMachine\GraphQLite\Fixtures\Integration\Types;
6+
7+
use TheCodingMachine\GraphQLite\Annotations\ExtendType;
8+
use TheCodingMachine\GraphQLite\Annotations\Field;
9+
use TheCodingMachine\GraphQLite\Fixtures\Integration\Models\Post;
10+
11+
/**
12+
* @ExtendType(class=Post::class)
13+
*/
14+
class PostType
15+
{
16+
/**
17+
* @Field()
18+
*/
19+
public function getId(Post $post): int
20+
{
21+
return (int) $post->id;
22+
}
23+
24+
/**
25+
* @Field()
26+
*/
27+
public function getTitle(Post $post): string
28+
{
29+
return $post->title;
30+
}
31+
}

0 commit comments

Comments
 (0)