Skip to content

Commit 68549e7

Browse files
committed
Update input object type docs and prepare v15.21.0
#1715
1 parent f41ed56 commit 68549e7

File tree

4 files changed

+134
-56
lines changed

4 files changed

+134
-56
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ You can find and compare releases at the [GitHub release page](https://github.co
99

1010
## Unreleased
1111

12+
## v15.21.0
13+
1214
### Added
1315

1416
- Add support for `@oneOf` input object directive - enables "input unions" where exactly one field must be provided https://github.com/webonyx/graphql-php/pull/1715

docs/type-definitions/inputs.md

Lines changed: 130 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Input Object Type Definition
22

3-
The GraphQL specification defines Input Object Type for complex inputs. It is similar to ObjectType
4-
except that it's fields have no **args** or **resolve** options and their **type** must be input type.
3+
The GraphQL specification defines the Input Object type for complex inputs.
4+
It is similar to the Object type, but its fields have no **args** or **resolve** options and their **type** must be input type.
55

66
## Writing Input Object Types
77

8-
In graphql-php **Input Object Type** is an instance of `GraphQL\Type\Definition\InputObjectType`
8+
In graphql-php, **Input Object Type** is an instance of `GraphQL\Type\Definition\InputObjectType`
99
(or one of its subclasses) which accepts configuration array in its constructor:
1010

1111
```php
@@ -37,25 +37,28 @@ Every field may be of other InputObjectType (thus complex hierarchies of inputs
3737

3838
The constructor of `InputObjectType` accepts an `array` with the following options:
3939

40-
| Option | Type | Notes |
41-
| ----------- | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
42-
| name | `string` | **Required.** Unique name of this object type within Schema |
43-
| fields | `array` or `callable` | **Required**. An array describing object fields or callable returning such an array (see below). |
44-
| description | `string` | Plain-text description of this type for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation) |
45-
| parseValue | `callable(array<string, mixed>): mixed` | Converts incoming values from their array representation to something else (e.g. a value object) |
40+
| Option | Type | Notes |
41+
|-------------|----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
42+
| name | `string` | **Required.** Unique name of this object type within Schema |
43+
| description | `string` | Plain-text description of this type for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation) |
44+
| isOneOf | `bool` | Indicates that an Input Object is a OneOf Input Object (and thus requires exactly one of its fields be provided). |
45+
| parseValue | `callable(array<string, mixed>): mixed` | Converts incoming values from their array representation to something else (e.g. a value object) |
46+
| fields | `iterable<FieldConfig>` or `callable(): iterable<FieldConfig>` | **Required**. An iterable describing object fields or callable returning such an iterable (see below). |
4647

47-
Every field is an array with following entries:
48+
Every entry in `fields` is an array with following entries:
4849

49-
| Option | Type | Notes |
50-
| ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
51-
| name | `string` | **Required.** Name of the input field. When not set - inferred from **fields** array key |
52-
| type | `Type` | **Required.** Instance of one of [Input Types](inputs.md) (**Scalar**, **Enum**, **InputObjectType** + any combination of those with **nonNull** and **listOf** modifiers) |
53-
| description | `string` | Plain-text description of this input field for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation) |
54-
| defaultValue | `scalar` | Default value of this input field. Use the internal value if specifying a default for an **enum** type |
50+
| Option | Type | Notes |
51+
|-------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
52+
| name | `string` | **Required.** Name of the input field. When not set - inferred from **fields** array key |
53+
| type | `Type` | **Required.** Instance of one of [Input Types](inputs.md) (**Scalar**, **Enum**, **InputObjectType** + any combination of those with **nonNull** and **listOf** modifiers) |
54+
| defaultValue | `scalar` | Default value of this input field. Use the internal value if specifying a default for an **enum** type |
55+
| description | `string` | Plain-text description of this input field for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation) |
56+
| deprecationReason | `string` | Plain-test reason for why this input field is deprecated. |
5557

5658
## Using Input Object Type
5759

58-
In the example above we defined our InputObjectType. Now let's use it in one of field arguments:
60+
In the example above we defined our InputObjectType `StoryFiltersInput`.
61+
Now let's use it in one of field arguments:
5962

6063
```php
6164
use GraphQL\Type\Definition\Type;
@@ -70,66 +73,68 @@ $queryType = new ObjectType([
7073
'filters' => [
7174
'type' => $filters,
7275
'defaultValue' => [
73-
'popular' => true
74-
]
75-
]
76+
'popular' => true,
77+
],
78+
],
7679
],
7780
'resolve' => fn ($rootValue, array $args): array => DataSource::filterStories($args['filters']),
78-
]
79-
]
81+
],
82+
],
8083
]);
8184
```
8285

83-
(note that you can define **defaultValue** for fields with complex inputs as associative array).
86+
You can define **defaultValue** for fields with complex inputs as an associative array.
8487

85-
Then GraphQL query could include filters as literal value:
88+
Then GraphQL query could include filters as a literal value:
8689

8790
```graphql
8891
{
89-
stories(filters: { author: "1", popular: false })
92+
stories(filters: {
93+
author: "1"
94+
popular: false
95+
}) {
96+
...
97+
}
9098
}
9199
```
92100

93-
Or as query variable:
101+
Or as a query variable:
94102

95103
```graphql
96104
query ($filters: StoryFiltersInput!) {
97-
stories(filters: $filters)
105+
stories(filters: $filters) {
106+
...
107+
}
98108
}
99109
```
100110

101-
```php
102-
$variables = [
103-
'filters' => [
104-
"author" => "1",
105-
"popular" => false
106-
]
107-
];
111+
```json
112+
{
113+
"filters": {
114+
"author": "1",
115+
"popular": false
116+
}
117+
}
108118
```
109119

110-
**graphql-php** will validate the input against your InputObjectType definition and pass it to your
111-
resolver as `$args['filters']`
120+
**graphql-php** will validate the input against your InputObjectType definition and pass it to your resolver as `$args['filters']`.
112121

113-
## Converting input object array to value object
122+
## Converting Input Array to Value Object
114123

115124
If you want more type safety you can choose to parse the input array into a value object.
116125

117126
```php
118127
use GraphQL\Type\Definition\Type;
119128
use GraphQL\Type\Definition\InputObjectType;
120129

121-
final class StoryFiltersInput
130+
final readonly class StoryFiltersInput
122131
{
123-
public string $author;
124-
public ?bool $popular;
125-
public array $tags;
126-
127-
public function __construct(string $author, ?bool $popular, array $tags)
128-
{
129-
$this->author = $author;
130-
$this->popular = $popular;
131-
$this->tag = $tag;
132-
}
132+
public function __construct(
133+
public string $author,
134+
public ?bool $popular,
135+
/** @var array<string> */
136+
public array $tags
137+
) {}
133138
}
134139

135140
$filters = new InputObjectType([
@@ -145,15 +150,86 @@ $filters = new InputObjectType([
145150
'type' => Type::nonNull(Type::listOf(Type::string())),
146151
]
147152
],
148-
'parseValue' => fn(array $values) => new StoryFiltersInput(
149-
$values['author'],
150-
$values['popular'] ?? null,
151-
$values['tags']
153+
'parseValue' => fn (array $values): StoryFiltersInput => new StoryFiltersInput(
154+
author: $values['author'],
155+
popular: $values['popular'] ?? null,
156+
tags: $values['tags'],
152157
),
153158
]);
154159
```
155160

156161
The value of `$args['filters']` will now be an instance of `StoryFiltersInput`.
157162

158-
The incoming values are converted using a depth-first traversal. Thus, nested input values
159-
will be passed through their respective `parseValue` functions before the parent receives their value.
163+
The incoming values are converted using a depth-first traversal.
164+
Thus, nested input values will be passed through their respective `parseValue` functions before the parent receives their value.
165+
166+
## Using the `isOneOf` Configuration Option
167+
168+
The `isOneOf` configuration option allows you to declare an input object as a *OneOf Input Object*.
169+
This means that exactly one of its fields must be provided when the input is used.
170+
This is useful when an argument can accept several alternative values, but never more than one at the same time.
171+
172+
Suppose you want to allow a query to filter stories either by author **or** by tag, but not both together.
173+
You can define an input object with `isOneOf: true`:
174+
175+
```php
176+
use GraphQL\Type\Definition\Type;
177+
use GraphQL\Type\Definition\InputObjectType;
178+
179+
$storySearch = new InputObjectType([
180+
'name' => 'StorySearchInput',
181+
'isOneOf' => true,
182+
'fields' => [
183+
'author' => [
184+
'type' => Type::id(),
185+
'description' => 'Find stories by a specific author',
186+
],
187+
'tag' => [
188+
'type' => Type::string(),
189+
'description' => 'Find stories with a specific tag',
190+
],
191+
],
192+
]);
193+
```
194+
195+
This input object can then be used as an argument in a query:
196+
197+
```php
198+
$searchType = new ObjectType([
199+
'name' => 'Query',
200+
'fields' => [
201+
'searchStories' => [
202+
'type' => Type::listOf($storyType),
203+
'args' => [
204+
'search' => [
205+
'type' => $storySearch,
206+
],
207+
],
208+
'resolve' => fn ($rootValue, array $args): array => DataSource::searchStories($args['search']),
209+
],
210+
],
211+
]);
212+
```
213+
214+
```graphql
215+
{
216+
searchStories(search: { author: "1" }) {
217+
id
218+
title
219+
}
220+
}
221+
```
222+
223+
If you try to provide both fields at once, validation will fail:
224+
225+
```graphql
226+
{
227+
searchStories(search: { author: "1", tag: "php" }) {
228+
id
229+
title
230+
}
231+
}
232+
```
233+
234+
**graphql-php** ensures that with `isOneOf: true`, exactly one field is set.
235+
This helps make APIs clearer and less error-prone when alternative inputs are required.

src/Type/Definition/Directive.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public static function oneOfDirective(): Directive
149149
{
150150
return self::$internalDirectives[self::ONE_OF_NAME] ??= new self([
151151
'name' => self::ONE_OF_NAME,
152-
'description' => 'Indicates that an input object is a oneof input object and exactly one of the input fields must be specified.',
152+
'description' => 'Indicates that an Input Object is a OneOf Input Object (and thus requires exactly one of its fields be provided).',
153153
'locations' => [
154154
DirectiveLocation::INPUT_OBJECT,
155155
],

tests/Utils/SchemaPrinterTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1011,7 +1011,7 @@ public function testPrintIntrospectionSchema(): void
10111011
reason: String = "No longer supported"
10121012
) on FIELD_DEFINITION | ENUM_VALUE | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
10131013
1014-
"Indicates that an input object is a oneof input object and exactly one of the input fields must be specified."
1014+
"Indicates that an Input Object is a OneOf Input Object (and thus requires exactly one of its fields be provided)."
10151015
directive @oneOf on INPUT_OBJECT
10161016
10171017
"A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations."

0 commit comments

Comments
 (0)