Skip to content

Commit 8209277

Browse files
committed
Add GraphQL context as 3rd argument to custom relation 'query'
Allows to mutate the context from within e.g. a GraphQL query and safely pass data down to custom relation queries. Fixes #459
1 parent 6647764 commit 8209277

File tree

12 files changed

+1265
-21
lines changed

12 files changed

+1265
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
[Next release](https://github.com/rebing/graphql-laravel/compare/2.0.1...master)
55
--------------
66
### Added
7+
- The custom `'query'` now receives the GraphQL context as the 3rd arg (same as any resolver) [\#464 / mfn](https://github.com/rebing/graphql-laravel/pull/464)
78
- Allow to load deeper nested queries by allowing to change the depth when calling `$getSelectFields(int $depth)` [\#472 / mfn](https://github.com/rebing/graphql-laravel/pull/472)
89

910
2019-08-18, 2.0.1

Readme.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -997,8 +997,15 @@ class UserType extends GraphQLType
997997
'posts' => [
998998
'type' => Type::listOf(GraphQL::type('post')),
999999
'description' => 'A list of posts written by the user',
1000-
// The first args are the parameters passed to the query
1001-
'query' => function(array $args, $query) {
1000+
'args' => [
1001+
'date_from' => [
1002+
'type' => Type::string(),
1003+
],
1004+
],
1005+
// $args are the local arguments passed to the relation
1006+
// $query is the relation builder object
1007+
// $ctx is the GraphQL context (can be customized by overriding `\Rebing\GraphQL\GraphQLController::queryContext`
1008+
'query' => function(array $args, $query, $ctx) {
10021009
return $query->where('posts.created_at', '>', $args['date_from']);
10031010
}
10041011
]

src/Support/Field.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,9 @@ protected function getResolver(): ?Closure
195195
// Add the 'selects and relations' feature as 5th arg
196196
if (isset($arguments[3])) {
197197
$arguments[] = function (int $depth = null) use ($arguments): SelectFields {
198-
return new SelectFields($arguments[3], $this->type(), $arguments[1], $depth ?? 5);
198+
$ctx = $arguments[2] ?? null;
199+
200+
return new SelectFields($arguments[3], $this->type(), $arguments[1], $depth ?? 5, $ctx);
199201
};
200202
}
201203

src/Support/SelectFields.php

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,17 @@ class SelectFields
3535
* @param GraphqlType $parentType
3636
* @param array $queryArgs Arguments given with the query/mutation
3737
* @param int $depth The depth to walk the AST and introspect for nested relations
38+
* @param mixed $ctx The GraphQL context; can be anything and is only passed through
39+
* Can be created/overridden by \Rebing\GraphQL\GraphQLController::queryContext
3840
*/
39-
public function __construct(ResolveInfo $info, GraphqlType $parentType, array $queryArgs, int $depth)
41+
public function __construct(ResolveInfo $info, GraphqlType $parentType, array $queryArgs, int $depth, $ctx)
4042
{
4143
if ($parentType instanceof WrappingType) {
4244
$parentType = $parentType->getWrappedType(true);
4345
}
4446

4547
$requestedFields = $this->getFieldSelection($info, $queryArgs, $depth);
46-
$fields = self::getSelectableFieldsAndRelations($queryArgs, $requestedFields, $parentType);
48+
$fields = self::getSelectableFieldsAndRelations($queryArgs, $requestedFields, $parentType, null, true, $ctx);
4749
$this->select = $fields[0];
4850
$this->relations = $fields[1];
4951
}
@@ -62,17 +64,24 @@ private function getFieldSelection(ResolveInfo $resolveInfo, array $args, int $d
6264
* Retrieve the fields (top level) and relations that
6365
* will be selected with the query.
6466
*
65-
* @param array $queryArgs Arguments given with the query/mutation
67+
* @param array $queryArgs Arguments given with the query/mutation
6668
* @param array $requestedFields
6769
* @param GraphqlType $parentType
6870
* @param Closure|null $customQuery
6971
* @param bool $topLevel
72+
* @param mixed $ctx The GraphQL context; can be anything and is only passed through
7073
* @return array|Closure - if first recursion, return an array,
7174
* where the first key is 'select' array and second is 'with' array.
7275
* On other recursions return a closure that will be used in with
7376
*/
74-
public static function getSelectableFieldsAndRelations(array $queryArgs, array $requestedFields, GraphqlType $parentType, ?Closure $customQuery = null, bool $topLevel = true)
75-
{
77+
public static function getSelectableFieldsAndRelations(
78+
array $queryArgs,
79+
array $requestedFields,
80+
GraphqlType $parentType,
81+
?Closure $customQuery = null,
82+
bool $topLevel = true,
83+
$ctx = null
84+
) {
7685
$select = [];
7786
$with = [];
7887

@@ -82,7 +91,7 @@ public static function getSelectableFieldsAndRelations(array $queryArgs, array $
8291
$parentTable = self::getTableNameFromParentType($parentType);
8392
$primaryKey = self::getPrimaryKeyFromParentType($parentType);
8493

85-
self::handleFields($queryArgs, $requestedFields, $parentType, $select, $with);
94+
self::handleFields($queryArgs, $requestedFields, $parentType, $select, $with, $ctx);
8695

8796
// If a primary key is given, but not in the selects, add it
8897
if (null !== $primaryKey) {
@@ -95,9 +104,9 @@ public static function getSelectableFieldsAndRelations(array $queryArgs, array $
95104
if ($topLevel) {
96105
return [$select, $with];
97106
} else {
98-
return function ($query) use ($with, $select, $customQuery, $requestedFields) {
107+
return function ($query) use ($with, $select, $customQuery, $requestedFields, $ctx) {
99108
if ($customQuery) {
100-
$query = $customQuery($requestedFields['args'], $query);
109+
$query = $customQuery($requestedFields['args'], $query, $ctx);
101110
}
102111

103112
$query->select($select);
@@ -115,9 +124,16 @@ public static function getSelectableFieldsAndRelations(array $queryArgs, array $
115124
* @param GraphqlType $parentType
116125
* @param array $select Passed by reference, adds further fields to select
117126
* @param array $with Passed by reference, adds further relations
127+
* @param mixed $ctx The GraphQL context; can be anything and is only passed through
118128
*/
119-
protected static function handleFields(array $queryArgs, array $requestedFields, GraphqlType $parentType, array &$select, array &$with): void
120-
{
129+
protected static function handleFields(
130+
array $queryArgs,
131+
array $requestedFields,
132+
GraphqlType $parentType,
133+
array &$select,
134+
array &$with,
135+
$ctx
136+
): void {
121137
$parentTable = self::isMongodbInstance($parentType) ? null : self::getTableNameFromParentType($parentType);
122138

123139
foreach ($requestedFields['fields'] as $key => $field) {
@@ -154,7 +170,7 @@ protected static function handleFields(array $queryArgs, array $requestedFields,
154170

155171
// Pagination
156172
if (is_a($parentType, config('graphql.pagination_type', PaginationType::class))) {
157-
self::handleFields($queryArgs, $field, $fieldObject->config['type']->getWrappedType(), $select, $with);
173+
self::handleFields($queryArgs, $field, $fieldObject->config['type']->getWrappedType(), $select, $with, $ctx);
158174
}
159175
// With
160176
elseif (is_array($field['fields']) && $queryable) {
@@ -206,9 +222,9 @@ protected static function handleFields(array $queryArgs, array $requestedFields,
206222

207223
self::addAlwaysFields($fieldObject, $field, $parentTable, true);
208224

209-
$with[$relationsKey] = self::getSelectableFieldsAndRelations($queryArgs, $field, $newParentType, $customQuery, false);
225+
$with[$relationsKey] = self::getSelectableFieldsAndRelations($queryArgs, $field, $newParentType, $customQuery, false, $ctx);
210226
} else {
211-
self::handleFields($queryArgs, $field, $fieldObject->config['type'], $select, $with);
227+
self::handleFields($queryArgs, $field, $fieldObject->config['type'], $select, $with, $ctx);
212228
}
213229
}
214230
// Select
@@ -310,8 +326,12 @@ private static function isQueryable(array $fieldObject): bool
310326
* @param string|null $parentTable
311327
* @param bool $forRelation
312328
*/
313-
protected static function addAlwaysFields(FieldDefinition $fieldObject, array &$select, ?string $parentTable, bool $forRelation = false): void
314-
{
329+
protected static function addAlwaysFields(
330+
FieldDefinition $fieldObject,
331+
array &$select,
332+
?string $parentTable,
333+
bool $forRelation = false
334+
): void {
315335
if (isset($fieldObject->config['always'])) {
316336
$always = $fieldObject->config['always'];
317337

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rebing\GraphQL\Tests\Database\SelectFields\QueryArgsAndContextTests;
6+
7+
use GraphQL\Type\Definition\Type;
8+
use Rebing\GraphQL\Support\Type as GraphQLType;
9+
use Rebing\GraphQL\Tests\Support\Models\Comment;
10+
11+
class CommentType extends GraphQLType
12+
{
13+
protected $attributes = [
14+
'name' => 'Comment',
15+
'model' => Comment::class,
16+
];
17+
18+
public function fields(): array
19+
{
20+
return [
21+
'id' => [
22+
'type' => Type::nonNull(Type::ID()),
23+
],
24+
'body' => [
25+
'type' => Type::string(),
26+
],
27+
'title' => [
28+
'type' => Type::nonNull(Type::string()),
29+
],
30+
];
31+
}
32+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rebing\GraphQL\Tests\Database\SelectFields\QueryArgsAndContextTests;
6+
7+
class GraphQLContext
8+
{
9+
public $data;
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Rebing\GraphQL\Tests\Database\SelectFields\QueryArgsAndContextTests;
6+
7+
use Rebing\GraphQL\GraphQLController as BaseGraphQLController;
8+
9+
class GraphQLController extends BaseGraphQLController
10+
{
11+
protected function queryContext(string $query, ?array $params, string $schema)
12+
{
13+
return new GraphQLContext();
14+
}
15+
}

0 commit comments

Comments
 (0)