Skip to content

Commit

Permalink
revert to previous Prefetch behavior
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrii Sudiev committed Sep 26, 2024
1 parent ee84251 commit ef75504
Show file tree
Hide file tree
Showing 10 changed files with 41 additions and 137 deletions.
10 changes: 2 additions & 8 deletions src/Annotations/Prefetch.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,8 @@
#[Attribute(Attribute::TARGET_PARAMETER)]
class Prefetch implements ParameterAnnotationInterface
{
/**
* @param string|(callable&array{class-string, string}) $callable
* @param bool $returnRequested return value mapped to requested method
*/
public function __construct(
public readonly string|array $callable,
public readonly bool $returnRequested = false,
)
/** @param string|(callable&array{class-string, string}) $callable */
public function __construct(public readonly string|array $callable)
{
}

Expand Down
1 change: 0 additions & 1 deletion src/Mappers/Parameters/PrefetchParameterMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public function mapParameter(ReflectionParameter $parameter, DocBlock $docBlock,
fieldName: $method->getName(),
resolver: $resolver,
parameters: $parameters,
returnRequested: $prefetch->returnRequested,
);
}
}
26 changes: 7 additions & 19 deletions src/Parameters/PrefetchDataParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
use TheCodingMachine\GraphQLite\PrefetchBuffer;
use TheCodingMachine\GraphQLite\QueryField;

use function array_key_exists;
use function assert;

/**
Expand All @@ -28,7 +27,6 @@ public function __construct(
private readonly string $fieldName,
private readonly mixed $resolver,
public readonly array $parameters,
public readonly bool $returnRequested = false,
)
{
}
Expand Down Expand Up @@ -59,7 +57,10 @@ public function resolve(object|null $source, array $args, mixed $context, Resolv
$this->computePrefetch($args, $context, $info, $prefetchBuffer);
}

return $prefetchBuffer->getResult($source);
$result = $prefetchBuffer->getResult($source);
// clear internal storage
$prefetchBuffer->purgeResult($source);
return $result;
});
}

Expand All @@ -71,22 +72,9 @@ private function computePrefetch(array $args, mixed $context, ResolveInfo $info,
$toPassPrefetchArgs = QueryField::paramsToArguments($this->fieldName, $this->parameters, null, $args, $context, $info, $this->resolver);

$resolvedValues = ($this->resolver)($sources, ...$toPassPrefetchArgs);
if ($this->returnRequested) {
foreach ($resolvedValues as $key => $resolvedValue) {
if (! array_key_exists($key, $sources)) {
throw new GraphQLRuntimeException(
'Called by Prefetch function should accept ' .
'Array<key> and return Array<value>, but the function did ' .
'not return an Array of the same length as the Array of keys.',
);
}
$prefetchBuffer->storeResult($sources[$key], $resolvedValue);
}
} else {
foreach ($sources as $source) {
// map results to each source to support old prefetch behavior
$prefetchBuffer->storeResult($source, $resolvedValues);
}
foreach ($sources as $source) {
// map results to each source to support old prefetch behavior
$prefetchBuffer->storeResult($source, $resolvedValues);
}
}

Expand Down
6 changes: 6 additions & 0 deletions src/PrefetchBuffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,10 @@ public function getResult(
): mixed {
return $this->results->offsetGet($source);
}

public function purgeResult(
object $source,
): void {
$this->results->offsetUnset($source);
}
}
7 changes: 5 additions & 2 deletions src/QueryField.php
Original file line number Diff line number Diff line change
Expand Up @@ -194,14 +194,17 @@ public static function paramsToArguments(string $name, array $parameters, object
*/
public static function deferred(array $toPassArgs, Closure $callResolver): mixed
{
$deferredArgument = null;
foreach ($toPassArgs as $position => $toPassArg) {
if ($toPassArg instanceof SyncPromise) {
return $toPassArg->then(static function ($resolvedValue) use ($toPassArgs, $position, $callResolver) {
$deferredArgument = $toPassArg->then(static function ($resolvedValue) use ($toPassArgs, $position, $callResolver) {
$toPassArgs[$position] = $resolvedValue;
return self::deferred($toPassArgs, $callResolver);
});
break;
}
}
return $callResolver(...$toPassArgs);

return $deferredArgument ?? $callResolver(...$toPassArgs);
}
}
20 changes: 10 additions & 10 deletions tests/Fixtures/Integration/Models/Blog.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,29 @@ public function getId(): int
}

/**
* @param Post[] $prefetchedPosts
* @param Post[][] $prefetchedPosts
*
* @return Post[]
*/
#[Field]
public function getPosts(
#[Prefetch('prefetchPosts', true)]
#[Prefetch('prefetchPosts')]
array $prefetchedPosts,
): array {
return $prefetchedPosts;
return $prefetchedPosts[$this->id];
}

/**
* @param Blog[] $prefetchedSubBlogs
* @param Blog[][] $prefetchedSubBlogs
*
* @return Blog[]
*/
#[Field]
public function getSubBlogs(
#[Prefetch('prefetchSubBlogs', true)]
#[Prefetch('prefetchSubBlogs')]
array $prefetchedSubBlogs,
): array {
return $prefetchedSubBlogs;
return $prefetchedSubBlogs[$this->id];
}

/**
Expand All @@ -56,9 +56,9 @@ public function getSubBlogs(
public static function prefetchPosts(iterable $blogs): array
{
$posts = [];
foreach ($blogs as $key => $blog) {
foreach ($blogs as $blog) {
$blogId = $blog->getId();
$posts[$key] = [
$posts[$blog->getId()] = [
new Post('post-' . $blogId . '.1'),
new Post('post-' . $blogId . '.2'),
];
Expand All @@ -75,10 +75,10 @@ public static function prefetchPosts(iterable $blogs): array
public static function prefetchSubBlogs(iterable $blogs): array
{
$subBlogs = [];
foreach ($blogs as $key => $blog) {
foreach ($blogs as $blog) {
$blogId = $blog->getId();
$subBlogId = $blogId * 10;
$subBlogs[$key] = [new Blog($subBlogId)];
$subBlogs[$blog->id] = [new Blog($subBlogId)];
}

return $subBlogs;
Expand Down
25 changes: 0 additions & 25 deletions tests/Fixtures/Integration/Types/CompanyType.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,4 @@ public static function prefetchContacts(array $companies): array

return $contacts;
}

#[Field]
public function getContactRequested(
Company $company,
#[Prefetch('prefetchContactsExact', true)]
Contact $contact,
): Contact|null {
return $contact;
}

/**
* @param Company[] $companies
*
* @return Contact[]
*/
public static function prefetchContactsExact(array $companies): array
{
$contacts = [];

foreach ($companies as $key => $company) {
$contacts[$key] = new Contact('Kate');
}

return $contacts;
}
}
15 changes: 9 additions & 6 deletions tests/Fixtures/Integration/Types/PostType.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,17 @@ public function getTitle(Post $post): string
}

/**
* @param Comment[] $prefetchedComments
* @param Comment[][] $prefetchedComments
*
* @return Comment[]
*/
#[Field]
public function getComments(
Post $post,
#[Prefetch('prefetchComments', true)]
#[Prefetch('prefetchComments')]
array $prefetchedComments,
): array {
return $prefetchedComments;
return $prefetchedComments[$post->title];
}

/**
Expand All @@ -49,11 +49,14 @@ public function getComments(
*
* @throws Exception
*/
public static function prefetchComments(array $posts): iterable
public static function prefetchComments(array $posts): Promise
{
$syncPromiseAdapter = new SyncPromiseAdapter();
foreach ($posts as $key => $post) {
yield $key => $syncPromiseAdapter->createFulfilled([new Comment('comment for ' . $post->title)]);
$result = [];
foreach ($posts as $post) {
$result[$post->title] = [new Comment('comment for ' . $post->title)];
}

return $syncPromiseAdapter->createFulfilled($result);
}
}
2 changes: 1 addition & 1 deletion tests/Parameters/PrefetchDataParameterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public function testResolveWithoutExistingResult(): void
self::assertFalse($buffer->hasResult($source));
self::assertSame([$source], $buffer->getObjectsByArguments($args));
self::assertSame($prefetchResult, $this->deferredValue($resolvedParameterPromise));
self::assertTrue($buffer->hasResult($source));
self::assertFalse($buffer->hasResult($source));
}

private function deferredValue(Deferred $promise): mixed
Expand Down
66 changes: 1 addition & 65 deletions website/docs/prefetch-method.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,83 +52,19 @@ class PostType {
{
// This method will receive the $prefetchedUsers as first argument. This is the return value of the "prefetchUsers" method below.
// Using this prefetched list, it should be easy to map it to the post
return $prefetchedUsers[$this->getUserId()];
}

/**
* @param Post[] $posts
* @return mixed
*/
public static function prefetchUsers(iterable $posts, #[Autowire] UserProvider $loader)
public static function prefetchUsers(iterable $posts)
{
// This function is called only once per GraphQL request
// with the list of posts. You can fetch the list of users
// associated with this posts in a single request,
// for instance using a "IN" query in SQL or a multi-fetch
// in your cache back-end.
$userIds = [];
foreach($posts as $post){
$userIds[] = $post->getUserId();
}
return $loader->loadMany($userIds)
}
}
```

When a `#[Prefetch]` attribute is constructed with returnRequested: true $prefetchedUser argument will be the mapped
value resolved for concrete PostType, so you dont need to extract value from prefetched values

```php
#[Type]
class PostType {
/**
* @param User $prefetchedUser
* @return User
*/
#[Field]
public function getUser(#[Prefetch("prefetchUsers", returnRequested: true)] $prefetchedUser): User
{
return $prefetchedUser;
}

/**
* @param Post[] $posts
* @return iterable
*/
public static function prefetchUsers(iterable $posts): iterable
{
foreach($posts as $post){
yield new User(...);
}
}
}
```


`#[Prefetch('prefetchUsers', true)]` callable can also return Promise[], so you can use for example Overblog DataLoader

```php
#[Type]
class PostType {
/**
* @param User $prefetchedUser
* @return User
*/
#[Field]
public function getUser(#[Prefetch("prefetchUsers", returnRequested: true)] $prefetchedUser): User
{
return $prefetchedUser;
}

/**
* @param Post[] $posts
* @return iterable
*/
public static function prefetchUsers(iterable $posts, #[Autowire] DataLoader $dataloader): iterable
{
foreach($posts as $post){
yield $dataloader->load($post->getId());
}
}
}
```
Expand Down

0 comments on commit ef75504

Please sign in to comment.