Skip to content

Commit f2748f1

Browse files
authored
Add field config decorator when building schema from SDL
1 parent 8ab8daa commit f2748f1

File tree

6 files changed

+102
-20
lines changed

6 files changed

+102
-20
lines changed

docs/class-reference.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2409,6 +2409,7 @@ Build instance of @see \GraphQL\Type\Schema out of schema language definition (s
24092409
See [schema definition language docs](schema-definition-language.md) for details.
24102410

24112411
@phpstan-import-type TypeConfigDecorator from ASTDefinitionBuilder
2412+
@phpstan-import-type FieldConfigDecorator from ASTDefinitionBuilder
24122413

24132414
@phpstan-type BuildSchemaOptions array{
24142415
assumeValid?: bool,
@@ -2439,6 +2440,7 @@ assumeValidSDL?: bool
24392440
* @param DocumentNode|Source|string $source
24402441
*
24412442
* @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
2443+
* @phpstan-param FieldConfigDecorator|null $fieldConfigDecorator
24422444
*
24432445
* @param array<string, bool> $options
24442446
*
@@ -2452,7 +2454,12 @@ assumeValidSDL?: bool
24522454
* @throws InvariantViolation
24532455
* @throws SyntaxError
24542456
*/
2455-
static function build($source, ?callable $typeConfigDecorator = null, array $options = []): GraphQL\Type\Schema
2457+
static function build(
2458+
$source,
2459+
?callable $typeConfigDecorator = null,
2460+
array $options = [],
2461+
?callable $fieldConfigDecorator = null
2462+
): GraphQL\Type\Schema
24562463
```
24572464

24582465
```php
@@ -2465,6 +2472,7 @@ static function build($source, ?callable $typeConfigDecorator = null, array $opt
24652472
* has no resolve methods, so execution will use default resolvers.
24662473
*
24672474
* @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
2475+
* @phpstan-param FieldConfigDecorator|null $fieldConfigDecorator
24682476
*
24692477
* @param array<string, bool> $options
24702478
*
@@ -2480,7 +2488,8 @@ static function build($source, ?callable $typeConfigDecorator = null, array $opt
24802488
static function buildAST(
24812489
GraphQL\Language\AST\DocumentNode $ast,
24822490
?callable $typeConfigDecorator = null,
2483-
array $options = []
2491+
array $options = [],
2492+
?callable $fieldConfigDecorator = null
24842493
): GraphQL\Type\Schema
24852494
```
24862495

src/Utils/ASTDefinitionBuilder.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
*
5353
* @phpstan-type ResolveType callable(string, Node|null): Type&NamedType
5454
* @phpstan-type TypeConfigDecorator callable(array<string, mixed>, Node&TypeDefinitionNode, array<string, Node&TypeDefinitionNode>): array<string, mixed>
55+
* @phpstan-type FieldConfigDecorator callable(UnnamedFieldDefinitionConfig, FieldDefinitionNode, ObjectTypeDefinitionNode|ObjectTypeExtensionNode|InterfaceTypeDefinitionNode|InterfaceTypeExtensionNode): UnnamedFieldDefinitionConfig
5556
*/
5657
class ASTDefinitionBuilder
5758
{
@@ -72,6 +73,13 @@ class ASTDefinitionBuilder
7273
*/
7374
private $typeConfigDecorator;
7475

76+
/**
77+
* @var callable|null
78+
*
79+
* @phpstan-var FieldConfigDecorator|null
80+
*/
81+
private $fieldConfigDecorator;
82+
7583
/** @var array<string, Type&NamedType> */
7684
private array $cache;
7785

@@ -91,12 +99,14 @@ public function __construct(
9199
array $typeDefinitionsMap,
92100
array $typeExtensionsMap,
93101
callable $resolveType,
94-
?callable $typeConfigDecorator = null
102+
?callable $typeConfigDecorator = null,
103+
?callable $fieldConfigDecorator = null
95104
) {
96105
$this->typeDefinitionsMap = $typeDefinitionsMap;
97106
$this->typeExtensionsMap = $typeExtensionsMap;
98107
$this->resolveType = $resolveType;
99108
$this->typeConfigDecorator = $typeConfigDecorator;
109+
$this->fieldConfigDecorator = $fieldConfigDecorator;
100110

101111
$this->cache = Type::builtInTypes();
102112
}
@@ -355,34 +365,42 @@ private function makeFieldDefMap(array $nodes): array
355365
$map = [];
356366
foreach ($nodes as $node) {
357367
foreach ($node->fields as $field) {
358-
$map[$field->name->value] = $this->buildField($field);
368+
$map[$field->name->value] = $this->buildField($field, $node);
359369
}
360370
}
361371

362372
return $map;
363373
}
364374

365375
/**
376+
* @param ObjectTypeDefinitionNode|ObjectTypeExtensionNode|InterfaceTypeDefinitionNode|InterfaceTypeExtensionNode $node
377+
*
366378
* @throws \Exception
367379
* @throws Error
368380
*
369381
* @return UnnamedFieldDefinitionConfig
370382
*/
371-
public function buildField(FieldDefinitionNode $field): array
383+
public function buildField(FieldDefinitionNode $field, object $node): array
372384
{
373385
// Note: While this could make assertions to get the correctly typed
374386
// value, that would throw immediately while type system validation
375387
// with validateSchema() will produce more actionable results.
376388
/** @var OutputType&Type $type */
377389
$type = $this->buildWrappedType($field->type);
378390

379-
return [
391+
$config = [
380392
'type' => $type,
381393
'description' => $field->description->value ?? null,
382394
'args' => $this->makeInputValues($field->arguments),
383395
'deprecationReason' => $this->getDeprecationReason($field),
384396
'astNode' => $field,
385397
];
398+
399+
if ($this->fieldConfigDecorator !== null) {
400+
$config = ($this->fieldConfigDecorator)($config, $field, $node);
401+
}
402+
403+
return $config;
386404
}
387405

388406
/**

src/Utils/BuildSchema.php

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
* See [schema definition language docs](schema-definition-language.md) for details.
2626
*
2727
* @phpstan-import-type TypeConfigDecorator from ASTDefinitionBuilder
28+
* @phpstan-import-type FieldConfigDecorator from ASTDefinitionBuilder
2829
*
2930
* @phpstan-type BuildSchemaOptions array{
3031
* assumeValid?: bool,
@@ -56,6 +57,13 @@ class BuildSchema
5657
*/
5758
private $typeConfigDecorator;
5859

60+
/**
61+
* @var callable|null
62+
*
63+
* @phpstan-var FieldConfigDecorator|null
64+
*/
65+
private $fieldConfigDecorator;
66+
5967
/**
6068
* @var array<string, bool>
6169
*
@@ -72,11 +80,13 @@ class BuildSchema
7280
public function __construct(
7381
DocumentNode $ast,
7482
?callable $typeConfigDecorator = null,
75-
array $options = []
83+
array $options = [],
84+
?callable $fieldConfigDecorator = null
7685
) {
7786
$this->ast = $ast;
7887
$this->typeConfigDecorator = $typeConfigDecorator;
7988
$this->options = $options;
89+
$this->fieldConfigDecorator = $fieldConfigDecorator;
8090
}
8191

8292
/**
@@ -86,6 +96,7 @@ public function __construct(
8696
* @param DocumentNode|Source|string $source
8797
*
8898
* @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
99+
* @phpstan-param FieldConfigDecorator|null $fieldConfigDecorator
89100
*
90101
* @param array<string, bool> $options
91102
*
@@ -102,13 +113,14 @@ public function __construct(
102113
public static function build(
103114
$source,
104115
?callable $typeConfigDecorator = null,
105-
array $options = []
116+
array $options = [],
117+
?callable $fieldConfigDecorator = null
106118
): Schema {
107119
$doc = $source instanceof DocumentNode
108120
? $source
109121
: Parser::parse($source);
110122

111-
return self::buildAST($doc, $typeConfigDecorator, $options);
123+
return self::buildAST($doc, $typeConfigDecorator, $options, $fieldConfigDecorator);
112124
}
113125

114126
/**
@@ -120,6 +132,7 @@ public static function build(
120132
* has no resolve methods, so execution will use default resolvers.
121133
*
122134
* @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
135+
* @phpstan-param FieldConfigDecorator|null $fieldConfigDecorator
123136
*
124137
* @param array<string, bool> $options
125138
*
@@ -135,9 +148,10 @@ public static function build(
135148
public static function buildAST(
136149
DocumentNode $ast,
137150
?callable $typeConfigDecorator = null,
138-
array $options = []
151+
array $options = [],
152+
?callable $fieldConfigDecorator = null
139153
): Schema {
140-
return (new self($ast, $typeConfigDecorator, $options))->buildSchema();
154+
return (new self($ast, $typeConfigDecorator, $options, $fieldConfigDecorator))->buildSchema();
141155
}
142156

143157
/**
@@ -200,7 +214,8 @@ public function buildSchema(): Schema
200214
static function (string $typeName): Type {
201215
throw self::unknownType($typeName);
202216
},
203-
$this->typeConfigDecorator
217+
$this->typeConfigDecorator,
218+
$this->fieldConfigDecorator
204219
);
205220

206221
$directives = \array_map(

src/Utils/SchemaExtender.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
/**
4141
* @phpstan-import-type TypeConfigDecorator from ASTDefinitionBuilder
42+
* @phpstan-import-type FieldConfigDecorator from ASTDefinitionBuilder
4243
* @phpstan-import-type UnnamedArgumentConfig from Argument
4344
* @phpstan-import-type UnnamedInputObjectFieldConfig from InputObjectField
4445
*
@@ -58,6 +59,7 @@ class SchemaExtender
5859
* @param array<string, bool> $options
5960
*
6061
* @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
62+
* @phpstan-param FieldConfigDecorator|null $fieldConfigDecorator
6163
*
6264
* @api
6365
*
@@ -68,15 +70,17 @@ public static function extend(
6870
Schema $schema,
6971
DocumentNode $documentAST,
7072
array $options = [],
71-
?callable $typeConfigDecorator = null
73+
?callable $typeConfigDecorator = null,
74+
?callable $fieldConfigDecorator = null
7275
): Schema {
73-
return (new static())->doExtend($schema, $documentAST, $options, $typeConfigDecorator);
76+
return (new static())->doExtend($schema, $documentAST, $options, $typeConfigDecorator, $fieldConfigDecorator);
7477
}
7578

7679
/**
7780
* @param array<string, bool> $options
7881
*
7982
* @phpstan-param TypeConfigDecorator|null $typeConfigDecorator
83+
* @phpstan-param FieldConfigDecorator|null $fieldConfigDecorator
8084
*
8185
* @throws \Exception
8286
* @throws \ReflectionException
@@ -87,7 +91,8 @@ protected function doExtend(
8791
Schema $schema,
8892
DocumentNode $documentAST,
8993
array $options = [],
90-
?callable $typeConfigDecorator = null
94+
?callable $typeConfigDecorator = null,
95+
?callable $fieldConfigDecorator = null
9196
): Schema {
9297
if (
9398
! ($options['assumeValid'] ?? false)
@@ -146,7 +151,8 @@ function (string $typeName) use ($schema): Type {
146151

147152
return $this->extendNamedType($existingType);
148153
},
149-
$typeConfigDecorator
154+
$typeConfigDecorator,
155+
$fieldConfigDecorator
150156
);
151157

152158
$this->extendTypeCache = [];
@@ -511,7 +517,7 @@ protected function extendFieldMap(Type $type): array
511517
);
512518

513519
foreach ($extension->fields as $field) {
514-
$newFieldMap[$field->name->value] = $this->astBuilder->buildField($field);
520+
$newFieldMap[$field->name->value] = $this->astBuilder->buildField($field, $extension);
515521
}
516522
}
517523
}

tests/Utils/BuildSchemaTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use GraphQL\Language\AST\DirectiveDefinitionNode;
1212
use GraphQL\Language\AST\DocumentNode;
1313
use GraphQL\Language\AST\EnumTypeDefinitionNode;
14+
use GraphQL\Language\AST\FieldDefinitionNode;
1415
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
1516
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
1617
use GraphQL\Language\AST\Node;
@@ -26,6 +27,7 @@
2627
use GraphQL\Type\Definition\Directive;
2728
use GraphQL\Type\Definition\EnumType;
2829
use GraphQL\Type\Definition\EnumValueDefinition;
30+
use GraphQL\Type\Definition\FieldDefinition;
2931
use GraphQL\Type\Definition\InputObjectType;
3032
use GraphQL\Type\Definition\InterfaceType;
3133
use GraphQL\Type\Definition\NamedType;
@@ -40,6 +42,9 @@
4042
use GraphQL\Utils\SchemaPrinter;
4143
use GraphQL\Validator\Rules\KnownDirectives;
4244

45+
/**
46+
* @phpstan-import-type UnnamedFieldDefinitionConfig from FieldDefinition
47+
*/
4348
final class BuildSchemaTest extends TestCaseBase
4449
{
4550
use ArraySubsetAsserts;
@@ -1340,7 +1345,15 @@ interface Hello {
13401345
return ['description' => 'My description of ' . $node->getName()->value] + $defaultConfig;
13411346
};
13421347

1343-
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator);
1348+
$fieldResolver = static fn (): string => 'OK';
1349+
$fieldConfigDecorator = static function (array $defaultConfig, FieldDefinitionNode $node) use (&$fieldResolver): array {
1350+
$defaultConfig['resolve'] = $fieldResolver;
1351+
1352+
/** @var UnnamedFieldDefinitionConfig $defaultConfig */
1353+
return $defaultConfig;
1354+
};
1355+
1356+
$schema = BuildSchema::buildAST($doc, $typeConfigDecorator, [], $fieldConfigDecorator);
13441357
$schema->getTypeMap();
13451358
self::assertSame(['Query', 'Color', 'Hello'], $decorated);
13461359

@@ -1358,6 +1371,10 @@ interface Hello {
13581371
self::assertInstanceOf(ObjectType::class, $query);
13591372
self::assertSame('My description of Query', $query->description);
13601373

1374+
self::assertSame($fieldResolver, $query->getFields()['str']->resolveFn);
1375+
self::assertSame($fieldResolver, $query->getFields()['color']->resolveFn);
1376+
self::assertSame($fieldResolver, $query->getFields()['hello']->resolveFn);
1377+
13611378
self::assertArrayHasKey(1, $calls);
13621379
[$defaultConfig, $node, $allNodesMap] = $calls[1]; // enum Color
13631380
self::assertInstanceOf(EnumTypeDefinitionNode::class, $node);

0 commit comments

Comments
 (0)