Skip to content

Commit

Permalink
Merge branch 'v2-beta-dataloader' into v2-beta
Browse files Browse the repository at this point in the history
  • Loading branch information
chrissm79 committed Mar 20, 2018
2 parents ee3957f + dcc1936 commit eef99ab
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 23 deletions.
30 changes: 30 additions & 0 deletions src/GraphQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public function execute($query, $context = null, $variables = [], $rootValue = n
$result = $this->queryAndReturnResult($query, $context, $variables, $rootValue);

if (! empty($result->errors)) {
foreach ($result->errors as $error) {
if ($error instanceof \Exception) {
info('GraphQL Error:', [
'code' => $error->getCode(),
'message' => $error->getMessage(),
'trace' => $error->getTraceAsString(),
]);
}
}

return [
'data' => $result->data,
'errors' => array_map([$this, 'formatError'], $result->errors),
Expand Down Expand Up @@ -112,6 +122,26 @@ public function buildSchema()
return $this->schema()->build($schema);
}

/**
* Batch field resolver.
*
* @param string $abstract
* @param mixed $key
* @param array $data
* @param string $name
*
* @return \GraphQL\Deferred
*/
public function batch($abstract, $key, array $data = [], $name = null)
{
$name = $name ?: $abstract;
$instance = app()->has($name)
? resolve($name)
: app()->instance($name, resolve($abstract));

return $instance->load($key, $data);
}

/**
* Get an instance of the schema builder.
*
Expand Down
7 changes: 6 additions & 1 deletion src/Schema/Directives/Fields/BelongsTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\DataLoader\Loaders\BelongsToLoader;
use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;

class BelongsTo implements FieldResolver
Expand Down Expand Up @@ -36,7 +37,11 @@ public function handle(FieldValue $value)
);

return $value->setResolver(function ($root, array $args) use ($relation) {
return $root->{$relation};
return graphql()->batch(BelongsToLoader::class, $root->getKey(), [
'relation' => $relation,
'root' => $root,
'args' => $args,
]);
});
}
}
35 changes: 13 additions & 22 deletions src/Schema/Directives/Fields/HasManyDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Nuwave\Lighthouse\Schema\Types\PaginatorField;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldResolver;
use Nuwave\Lighthouse\Support\DataLoader\Loaders\HasManyLoader;
use Nuwave\Lighthouse\Support\Exceptions\DirectiveException;
use Nuwave\Lighthouse\Support\Traits\CanParseTypes;
use Nuwave\Lighthouse\Support\Traits\HandlesDirectives;
Expand Down Expand Up @@ -141,13 +142,10 @@ protected function connectionTypeResolver($relation, FieldValue $value)
});

return function ($parent, array $args, $context = null, ResolveInfo $info = null) use ($relation, $scopes) {
$builder = call_user_func([$parent, $relation]);

return $builder->when(! empty($scopes), function ($q) use ($scopes, $args) {
foreach ($scopes as $scope) {
call_user_func_array([$q, $scope], [$args]);
}
})->relayConnection($args);
return graphql()->batch(HasManyLoader::class, $parent->getKey(), array_merge(
compact('relation', 'parent', 'args', 'scopes'),
['type' => 'relay']
), camel_case($parent->getTable().'_'.$relation));
};
}

Expand Down Expand Up @@ -195,13 +193,10 @@ protected function paginatorTypeResolver($relation, FieldValue $value)
});

return function ($parent, array $args, $context = null, ResolveInfo $info = null) use ($relation, $scopes) {
$builder = call_user_func([$parent, $relation]);

return $builder->when(! empty($scopes), function ($q) use ($scopes, $args) {
foreach ($scopes as $scope) {
call_user_func_array([$q, $scope], [$args]);
}
})->paginatorConnection($args);
return graphql()->batch(HasManyLoader::class, $parent->getKey(), array_merge(
compact('relation', 'parent', 'args', 'scopes'),
['type' => 'paginator']
), camel_case($parent->getTable().'_'.$relation));
};
}

Expand All @@ -218,14 +213,10 @@ protected function defaultResolver($relation, FieldValue $value)
$scopes = $this->getScopes($value);

return function ($parent, array $args) use ($relation, $scopes) {
// TODO: Wrap w/ data loader to prevent N+1
$builder = call_user_func([$parent, $relation]);
// TODO: Create scopeGqlQuery scope to allow adjustments for $args.
return $builder->when(! empty($scopes), function ($q) use ($scopes, $args) {
foreach ($scopes as $scope) {
call_user_func_array([$q, $scope], [$args]);
}
})->get();
return graphql()->batch(HasManyLoader::class, $parent->getKey(), array_merge(
compact('relation', 'parent', 'args', 'scopes'),
['type' => 'default']
), camel_case($parent->getTable().'_'.$relation));
};
}

Expand Down
62 changes: 62 additions & 0 deletions src/Support/DataLoader/BatchLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Nuwave\Lighthouse\Support\DataLoader;

use GraphQL\Deferred;

abstract class BatchLoader
{
/**
* Keys to resolve.
*
* @var \Illuminate\Support\Collection
*/
protected $keys = [];

/**
* Check if data has been loaded.
*
* @var bool
*/
protected $hasLoaded = false;

/**
* Load object by key.
*
* @param mixed $key
* @param array $data
*
* @return Deferred
*/
public function load($key, array $data = [])
{
$this->keys[$key] = $data;

return new Deferred(function () use ($key) {
if (! $this->hasLoaded) {
$this->resolve();
$this->hasLoaded = true;
}

return array_get($this->keys, "$key.value");
});
}

/**
* Set key value.
*
* @param mixed $key
* @param mixed $value
*/
protected function set($key, $value)
{
if ($field = array_get($this->keys, $key)) {
$this->keys[$key] = array_merge($field, compact('value'));
}
}

/**
* Resolve keys.
*/
abstract public function resolve();
}
26 changes: 26 additions & 0 deletions src/Support/DataLoader/Loaders/BelongsToLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Nuwave\Lighthouse\Support\DataLoader\Loaders;

use Nuwave\Lighthouse\Support\DataLoader\BatchLoader;

class BelongsToLoader extends BatchLoader
{
/**
* Resolve keys.
*/
public function resolve()
{
collect($this->keys)->map(function ($item) {
return array_merge($item, ['json' => json_encode($item['args'])]);
})->groupBy('json')->each(function ($items) {
$relation = array_get($items->first(), 'relation');
$models = $items->pluck('root');

$models->fetch([$relation]);
$models->each(function ($model) use ($relation) {
$this->set($model->id, $model->getRelation($relation));
});
});
}
}
55 changes: 55 additions & 0 deletions src/Support/DataLoader/Loaders/HasManyLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Nuwave\Lighthouse\Support\DataLoader\Loaders;

use Nuwave\Lighthouse\Support\DataLoader\BatchLoader;
use Nuwave\Lighthouse\Support\Traits\HandlesGlobalId;

class HasManyLoader extends BatchLoader
{
use HandlesGlobalId;

/**
* Resolve keys.
*/
public function resolve()
{
collect($this->keys)->map(function ($item) {
return array_merge($item, ['json' => json_encode($item['args'])]);
})->groupBy('json')->each(function ($items) {
$first = $items->first();
$parents = $items->pluck('parent');
$scopes = array_get($first, 'scopes', []);
$relation = $first['relation'];
$type = $first['type'];
$args = $first['args'];

$constraints = [$relation => function ($q) use ($scopes, $args) {
foreach ($scopes as $scope) {
call_user_func_array([$q, $scope], [$args]);
}
}];

switch ($type) {
case 'relay':
$first = data_get($args, 'first', 15);
$after = $this->decodeCursor($args);
$currentPage = $first && $after ? floor(($first + $after) / $first) : 1;
$parents->fetchForPage($first, $currentPage, $constraints);
break;
case 'paginator':
$first = data_get($args, 'count', 15);
$page = data_get($args, 'page', 1);
$parents->fetchForPage($first, $page, $constraints);
break;
default:
$parents->fetch($constraints);
break;
}

$parents->each(function ($model) use ($relation) {
$this->set($model->id, $model->getRelation($relation));
});
});
}
}

0 comments on commit eef99ab

Please sign in to comment.