diff --git a/lib/promise-adapter/src/Adapter/WebonyxGraphQLSyncPromiseAdapter.php b/lib/promise-adapter/src/Adapter/WebonyxGraphQLSyncPromiseAdapter.php index 7fb5eaa..dd60566 100644 --- a/lib/promise-adapter/src/Adapter/WebonyxGraphQLSyncPromiseAdapter.php +++ b/lib/promise-adapter/src/Adapter/WebonyxGraphQLSyncPromiseAdapter.php @@ -32,7 +32,7 @@ class WebonyxGraphQLSyncPromiseAdapter implements PromiseAdapterInterface public function __construct(?SyncPromiseAdapter $webonyxPromiseAdapter = null) { - $webonyxPromiseAdapter = $webonyxPromiseAdapter?:new SyncPromiseAdapter(); + $webonyxPromiseAdapter = $webonyxPromiseAdapter?:new \Overblog\DataLoader\Promise\Adapter\Webonyx\GraphQL\SyncPromiseAdapter(); $this->setWebonyxPromiseAdapter($webonyxPromiseAdapter); } diff --git a/src/DataLoader.php b/src/DataLoader.php index a016c68..74526de 100644 --- a/src/DataLoader.php +++ b/src/DataLoader.php @@ -355,18 +355,11 @@ function ($values) use ($keys, $queue) { sprintf('not return a Promise of an Array: %s.', gettype($values)) ); } - if (count($values) !== count($keys)) { - throw new \RuntimeException( - 'DataLoader must be constructed with a function which accepts ' . - 'Array and returns Promise>, but the function did ' . - 'not return a Promise of an Array of the same length as the Array of keys.' - ); - } // Step through the values, resolving or rejecting each Promise in the // loaded queue. - foreach ($queue as $index => $data) { - $value = $values[$index]; + foreach ($queue as $data) { + $value = $values[$data['key']] ?? null; if ($value instanceof \Throwable) { $data['reject']($value); } else { diff --git a/src/Promise/Adapter/Webonyx/GraphQL/SyncPromiseAdapter.php b/src/Promise/Adapter/Webonyx/GraphQL/SyncPromiseAdapter.php index da42184..314ae51 100644 --- a/src/Promise/Adapter/Webonyx/GraphQL/SyncPromiseAdapter.php +++ b/src/Promise/Adapter/Webonyx/GraphQL/SyncPromiseAdapter.php @@ -11,9 +11,17 @@ namespace Overblog\DataLoader\Promise\Adapter\Webonyx\GraphQL; +use Countable; +use Ds\Map; +use Exception; +use GraphQL\Error\InvariantViolation; +use GraphQL\Executor\Promise\Adapter\SyncPromise; use GraphQL\Executor\Promise\Adapter\SyncPromiseAdapter as BaseSyncPromiseAdapter; use GraphQL\Executor\Promise\Promise; use Overblog\DataLoader\DataLoader; +use Throwable; +use function assert; +use function is_array; class SyncPromiseAdapter extends BaseSyncPromiseAdapter { @@ -26,4 +34,44 @@ protected function onWait(Promise $promise): void { DataLoader::await(); } + + /** @throws InvariantViolation */ + public function all(iterable $promisesOrValues): Promise + { + assert(is_countable($promisesOrValues)); + + $all = new SyncPromise(); + + $total = count($promisesOrValues); + $count = 0; + /** @var Map $result */ + $result = new Map(); + + $resolveAllWhenFinished = function () use (&$count, &$total, $all, &$result, $promisesOrValues): void { + if ($count === $total) { + $all->resolve(is_array($promisesOrValues) ? $result->toArray() : $result); + } + }; + + foreach ($promisesOrValues as $index => $promiseOrValue) { + if ($promiseOrValue instanceof Promise) { + $result->put($index, null); + $promiseOrValue->then( + static function ($value) use ($result, $index, &$count, &$resolveAllWhenFinished): void { + $result->put($index, $value); + ++$count; + $resolveAllWhenFinished(); + }, + static fn (Throwable $reason): SyncPromise => $all->reject($reason) + ); + } else { + $result->put($index, $promiseOrValue); + ++$count; + } + } + + $resolveAllWhenFinished(); + + return new Promise($all, $this); + } }