mirror of
https://github.com/retailcrm/graphql-php.git
synced 2025-02-16 12:23:15 +03:00
Further documentation improvements
This commit is contained in:
parent
2537a62ec2
commit
637156fe65
14
UPGRADE.md
14
UPGRADE.md
@ -1,6 +1,6 @@
|
|||||||
## Upgrade v0.8.x, v0.9.x > v0.10.x
|
## Upgrade v0.8.x, v0.9.x > v0.10.x
|
||||||
|
|
||||||
### Breaking: minimum PHP version was changed from 5.4 to 5.5
|
### Breaking: changed minimum PHP version from 5.4 to 5.5
|
||||||
It allows us to leverage `::class` constant, `generators` and other features of newer PHP versions.
|
It allows us to leverage `::class` constant, `generators` and other features of newer PHP versions.
|
||||||
|
|
||||||
### Breaking: default error formatting
|
### Breaking: default error formatting
|
||||||
@ -71,8 +71,9 @@ to adjust if you were checking for this error in your custom error formatters.
|
|||||||
### Breaking: removed previously deprecated ability to define type as callable
|
### Breaking: removed previously deprecated ability to define type as callable
|
||||||
See https://github.com/webonyx/graphql-php/issues/35
|
See https://github.com/webonyx/graphql-php/issues/35
|
||||||
|
|
||||||
### Deprecated: `GraphQL\GraphQL::executeAndReturnResult` renamed to `GraphQL\GraphQL::executeQuery`
|
### Deprecated: `GraphQL\GraphQL::executeAndReturnResult`
|
||||||
Old method name is still available, but will trigger deprecation warning in next version.
|
Method is renamed to `GraphQL\GraphQL::executeQuery`. Old method name is still available,
|
||||||
|
but will trigger deprecation warning in the next version.
|
||||||
|
|
||||||
### Deprecated: `GraphQL\GraphQL::execute`
|
### Deprecated: `GraphQL\GraphQL::execute`
|
||||||
Use `GraphQL\GraphQL::executeQuery()->toArray()` instead.
|
Use `GraphQL\GraphQL::executeQuery()->toArray()` instead.
|
||||||
@ -95,6 +96,13 @@ $schema->assertValid();
|
|||||||
```
|
```
|
||||||
See https://github.com/webonyx/graphql-php/issues/148
|
See https://github.com/webonyx/graphql-php/issues/148
|
||||||
|
|
||||||
|
### Non-breaking: usage on async platforms
|
||||||
|
When using the library on async platforms use separate method `GraphQL::promiseToExecute()`.
|
||||||
|
It requires promise adapter in it's first argument and always returns a `Promise`.
|
||||||
|
|
||||||
|
Old methods `GraphQL::execute` and `GraphQL::executeAndReturnResult` still work in backwards-compatible manner,
|
||||||
|
but they are deprecated and will be removed eventually.
|
||||||
|
|
||||||
## Upgrade v0.7.x > v0.8.x
|
## Upgrade v0.7.x > v0.8.x
|
||||||
All of those changes apply to those who extends various parts of this library.
|
All of those changes apply to those who extends various parts of this library.
|
||||||
If you only use the library and don't try to extend it - everything should work without breaks.
|
If you only use the library and don't try to extend it - everything should work without breaks.
|
||||||
|
@ -3,10 +3,10 @@ GraphQL is data-storage agnostic. You can use any underlying data storage engine
|
|||||||
plain files or in-memory data structures.
|
plain files or in-memory data structures.
|
||||||
|
|
||||||
In order to convert GraphQL query to PHP array **graphql-php** traverses query fields (using depth-first algorithm) and
|
In order to convert GraphQL query to PHP array **graphql-php** traverses query fields (using depth-first algorithm) and
|
||||||
runs special `resolve` function on each field. This `resolve` function is provided by you as a part of
|
runs special **resolve** function on each field. This **resolve** function is provided by you as a part of
|
||||||
[field definition](type-system/object-types/#field-configuration-options) or [query execution call](executing-queries/#overview).
|
[field definition](type-system/object-types/#field-configuration-options) or [query execution call](executing-queries/#overview).
|
||||||
|
|
||||||
Result returned by `resolve` function is directly included in response (for scalars and enums)
|
Result returned by **resolve** function is directly included in response (for scalars and enums)
|
||||||
or passed down to nested fields (for objects).
|
or passed down to nested fields (for objects).
|
||||||
|
|
||||||
Let's walk through an example. Consider following GraphQL query:
|
Let's walk through an example. Consider following GraphQL query:
|
||||||
@ -22,7 +22,7 @@ Let's walk through an example. Consider following GraphQL query:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We need Schema that can fulfill it. On the very top level Schema contains Query type:
|
We need a Schema that can fulfill it. On the very top level the Schema contains Query type:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$queryType = new ObjectType([
|
$queryType = new ObjectType([
|
||||||
@ -44,12 +44,12 @@ $queryType = new ObjectType([
|
|||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
As we see field `lastStory` has `resolve` function that is responsible for fetching data.
|
As we see field **lastStory** has **resolve** function that is responsible for fetching data.
|
||||||
|
|
||||||
In our example we simply return array value, but in real-world application you would query
|
In our example we simply return array value, but in real-world application you would query
|
||||||
your database/cache/search index and return result.
|
your database/cache/search index and return result.
|
||||||
|
|
||||||
Since `lastStory` is of complex type `BlogStory` this result is passed down to fields of this type:
|
Since **lastStory** is of composite type **BlogStory** this result is passed down to fields of this type:
|
||||||
|
|
||||||
```php
|
```php
|
||||||
$blogStoryType = new ObjectType([
|
$blogStoryType = new ObjectType([
|
||||||
@ -81,15 +81,15 @@ $blogStoryType = new ObjectType([
|
|||||||
]);
|
]);
|
||||||
```
|
```
|
||||||
|
|
||||||
Here `$blogStory` is the array returned by `lastStory` field above.
|
Here **$blogStory** is the array returned by **lastStory** field above.
|
||||||
|
|
||||||
Again: in real-world applications you would fetch user data from datastore by `authorId` and return it.
|
Again: in real-world applications you would fetch user data from datastore by **authorId** and return it.
|
||||||
Also note that you don't have to return arrays. You can return any value, **graphql-php** will pass it untouched
|
Also note that you don't have to return arrays. You can return any value, **graphql-php** will pass it untouched
|
||||||
to nested resolvers.
|
to nested resolvers.
|
||||||
|
|
||||||
But then the question appears - field `title` has no `resolve` option. How is it resolved?
|
But then the question appears - field **title** has no **resolve** option. How is it resolved?
|
||||||
|
|
||||||
The answer is: there is default resolver for all fields. When you define your own `resolve` function
|
There is a default resolver for all fields. When you define your own **resolve** function
|
||||||
for a field you simply override this default resolver.
|
for a field you simply override this default resolver.
|
||||||
|
|
||||||
# Default Field Resolver
|
# Default Field Resolver
|
||||||
@ -114,12 +114,10 @@ function defaultFieldResolver($source, $args, $context, ResolveInfo $info)
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
As you see it returns value by key (for arrays) or property (for objects). If value is not set - it returns `null`.
|
As you see it returns value by key (for arrays) or property (for objects).
|
||||||
|
If value is not set - it returns **null**.
|
||||||
|
|
||||||
To override default resolver - use:
|
To override the default resolver, pass it as an argument of [executeQuery](executing-queries) call.
|
||||||
```php
|
|
||||||
GraphQL\GraphQL::setDefaultFieldResolver($myResolverCallback);
|
|
||||||
```
|
|
||||||
|
|
||||||
# Default Field Resolver per Type
|
# Default Field Resolver per Type
|
||||||
Sometimes it might be convenient to set default field resolver per type. You can do so by providing
|
Sometimes it might be convenient to set default field resolver per type. You can do so by providing
|
||||||
@ -154,8 +152,8 @@ Keep in mind that **field resolver** has precedence over **default field resolve
|
|||||||
# Solving N+1 Problem
|
# Solving N+1 Problem
|
||||||
Since: 0.9.0
|
Since: 0.9.0
|
||||||
|
|
||||||
One of the most annoying problems with data fetching is so-called [N+1 problem](https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/).
|
One of the most annoying problems with data fetching is a so-called
|
||||||
|
[N+1 problem](https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/). <br>
|
||||||
Consider following GraphQL query:
|
Consider following GraphQL query:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
@ -219,28 +217,41 @@ In this example only one query will be executed for all story authors comparing
|
|||||||
in naive implementation.
|
in naive implementation.
|
||||||
|
|
||||||
# Async PHP
|
# Async PHP
|
||||||
Since: 0.9.0
|
Since: 0.10.0 (version 0.9.0 had slightly different API which is deprecated)
|
||||||
|
|
||||||
If your project runs in environment that supports async operations
|
If your project runs in environment that supports async operations
|
||||||
(like `HHVM`, `ReactPHP`, `Icicle.io`, `appserver.io` `PHP threads`, etc) you can leverage
|
(like HHVM, ReactPHP, Icicle.io, appserver.io, PHP threads, etc)
|
||||||
the power of your platform to resolve fields asynchronously.
|
you can leverage the power of your platform to resolve some fields asynchronously.
|
||||||
|
|
||||||
The only requirement: your platform must support the concept of Promises compatible with
|
The only requirement: your platform must support the concept of Promises compatible with
|
||||||
[Promises A+](https://promisesaplus.com/) specification.
|
[Promises A+](https://promisesaplus.com/) specification.
|
||||||
|
|
||||||
To enable async support - set adapter for promises:
|
To start using this feature, switch facade method for query execution from
|
||||||
```
|
**executeQuery** to **promiseToExecute**:
|
||||||
GraphQL\GraphQL::setPromiseAdapter($adapter);
|
|
||||||
|
```php
|
||||||
|
$promise = GraphQL::promiseToExecute(
|
||||||
|
$promiseAdapter,
|
||||||
|
$schema,
|
||||||
|
$queryString,
|
||||||
|
$rootValue = null,
|
||||||
|
$contextValue = null,
|
||||||
|
$variableValues = null,
|
||||||
|
$operationName = null,
|
||||||
|
$fieldResolver = null,
|
||||||
|
$validationRules = null
|
||||||
|
);
|
||||||
|
$promise->then(function(ExecutionResult $result) {
|
||||||
|
return $result->toArray();
|
||||||
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Where `$adapter` is an instance of class implementing `GraphQL\Executor\Promise\PromiseAdapter` interface.
|
Where **$promiseAdapter** is an instance of:
|
||||||
|
|
||||||
Then in your `resolve` functions you should return `Promises` of your platform instead of
|
* For [ReactPHP](https://github.com/reactphp/react) (requires **react/promise** as composer dependency): <br>
|
||||||
`GraphQL\Deferred` instances.
|
`GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter`
|
||||||
|
|
||||||
Platforms supported out of the box:
|
* Other platforms: write your own class implementing interface: <br>
|
||||||
|
`GraphQL\Executor\Promise\PromiseAdapter`.
|
||||||
|
|
||||||
* [ReactPHP](https://github.com/reactphp/react) (requires **react/promise** as composer dependency):
|
Then your **resolve** functions should return promises of your platform instead of `GraphQL\Deferred`s.
|
||||||
`GraphQL\GraphQL::setPromiseAdapter(new GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter());`
|
|
||||||
|
|
||||||
To integrate other platform - implement `GraphQL\Executor\Promise\PromiseAdapter` interface.
|
|
@ -48,4 +48,7 @@ Schema Language parser.
|
|||||||
Ready for real-world usage.
|
Ready for real-world usage.
|
||||||
|
|
||||||
## Github
|
## Github
|
||||||
Project source code is [hosted on GitHub](https://github.com/webonyx/graphql-php).
|
Project source code is [hosted on GitHub](https://github.com/webonyx/graphql-php).
|
||||||
|
|
||||||
|
## Framework Integrations
|
||||||
|
Read the section about [Complementary tools](complementary-tools/).
|
@ -21,8 +21,7 @@ class GraphQL
|
|||||||
$variableValues = null,
|
$variableValues = null,
|
||||||
$operationName = null,
|
$operationName = null,
|
||||||
callable $fieldResolver = null,
|
callable $fieldResolver = null,
|
||||||
array $validationRules = null,
|
array $validationRules = null
|
||||||
GraphQL\Executor\Promise\PromiseAdapter $promiseAdapter = null
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -502,22 +501,20 @@ use GraphQL\Executor\Promise\PromiseAdapter;
|
|||||||
class Executor
|
class Executor
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Executes DocumentNode against given schema.
|
* Executes DocumentNode against given $schema using given $promiseAdapter for deferred resolvers.
|
||||||
|
* Returns promise which is always fullfilled with instance of ExecutionResult
|
||||||
*
|
*
|
||||||
* When $promiseAdapter is passed returns Promise instance produced by this adapter.
|
* @return Promise
|
||||||
* By default simply returns ExecutionResult
|
|
||||||
*
|
|
||||||
* @return ExecutionResult|Promise
|
|
||||||
*/
|
*/
|
||||||
public static function execute(
|
public static function promiseToExecute(
|
||||||
|
PromiseAdapter $promiseAdapter,
|
||||||
Schema $schema,
|
Schema $schema,
|
||||||
DocumentNode $ast,
|
DocumentNode $ast,
|
||||||
$rootValue = null,
|
$rootValue = null,
|
||||||
$contextValue = null,
|
$contextValue = null,
|
||||||
$variableValues = null,
|
$variableValues = null,
|
||||||
$operationName = null,
|
$operationName = null,
|
||||||
callable $fieldResolver = null,
|
callable $fieldResolver = null
|
||||||
PromiseAdapter $promiseAdapter = null
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -601,6 +598,80 @@ class ExecutionResult implements \JsonSerializable
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# GraphQL\Executor\Promise\PromiseAdapter
|
||||||
|
Required for [Async PHP](data-fetching/#async-php) only.
|
||||||
|
|
||||||
|
```php
|
||||||
|
interface PromiseAdapter
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Return true if value is promise of underlying system
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isThenable($value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts thenable of underlying system into Promise instance
|
||||||
|
*
|
||||||
|
* @param object $thenable
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function convertThenable($thenable);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts our Promise wrapper, extracts adopted promise out of it and executes actual `then` logic described
|
||||||
|
* in Promises/A+ specs. Then returns new wrapped Promise instance.
|
||||||
|
*
|
||||||
|
* @param Promise $promise
|
||||||
|
* @param callable|null $onFulfilled
|
||||||
|
* @param callable|null $onRejected
|
||||||
|
*
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function then(Promise $promise, callable $onFulfilled = null, callable $onRejected = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Promise
|
||||||
|
*
|
||||||
|
* @param callable $resolver
|
||||||
|
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function create(callable $resolver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a fulfilled Promise for a value if the value is not a promise.
|
||||||
|
*
|
||||||
|
* @param mixed $value
|
||||||
|
*
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function createFulfilled($value = null);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a rejected promise for a reason if the reason is not a promise. If
|
||||||
|
* the provided reason is a promise, then it is returned as-is.
|
||||||
|
*
|
||||||
|
* @param \Throwable $reason
|
||||||
|
*
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function createRejected($reason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an array of promises (or values), returns a promise that is fulfilled when all the
|
||||||
|
* items in the array are fulfilled.
|
||||||
|
*
|
||||||
|
* @param array $promisesOrValues Promises or values.
|
||||||
|
*
|
||||||
|
* @return Promise
|
||||||
|
*/
|
||||||
|
public function all(array $promisesOrValues);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
# GraphQL\Type\Definition\ResolveInfo
|
# GraphQL\Type\Definition\ResolveInfo
|
||||||
|
|
||||||
```php
|
```php
|
||||||
@ -910,3 +981,9 @@ class FormattedError
|
|||||||
public static function prepareFormatter(callable $formatter = null, $debug);
|
public static function prepareFormatter(callable $formatter = null, $debug);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
# GraphQL\Server\OperationParams
|
||||||
|
|
||||||
|
# GraphQL\Server\StandardServer
|
||||||
|
|
||||||
|
# GraphQL\Server\Helper
|
||||||
|
@ -50,34 +50,26 @@ class MyType extends ObjectType
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also mix-and-match styles for convenience. For example:
|
Using [GraphQL Type language](graphql.org/learn/schema/#type-language):
|
||||||
```php
|
|
||||||
<?php
|
|
||||||
namespace MyApp;
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\ObjectType;
|
```graphql
|
||||||
use GraphQL\Type\Definition\Type;
|
schema {
|
||||||
|
query: Query
|
||||||
|
mutation: Mutation
|
||||||
|
}
|
||||||
|
|
||||||
class BlogPostType extends ObjectType
|
type Query {
|
||||||
{
|
greetings(input: HelloInput!): String!
|
||||||
public function __construct()
|
}
|
||||||
{
|
|
||||||
$config = [
|
input HelloInput {
|
||||||
'fields' => [
|
firstName: String!
|
||||||
'body' => new ObjectType([
|
lastName: String
|
||||||
'name' => 'BlogPostBody',
|
|
||||||
'fields' => [
|
|
||||||
'html' => Type::string(),
|
|
||||||
'text' => Type::string(),
|
|
||||||
]
|
|
||||||
])
|
|
||||||
]
|
|
||||||
];
|
|
||||||
parent::__construct($config);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
[Read more](/type-system/type-language/) about it in a dedicated docs section.
|
||||||
|
|
||||||
# Type Registry
|
# Type Registry
|
||||||
Every type must be presented in Schema by single instance (**graphql-php**
|
Every type must be presented in Schema by single instance (**graphql-php**
|
||||||
throws when it discovers several instances with the same `name` in schema).
|
throws when it discovers several instances with the same `name` in schema).
|
||||||
|
@ -42,6 +42,7 @@ Here is an example of simple `Email` type:
|
|||||||
namespace MyApp;
|
namespace MyApp;
|
||||||
|
|
||||||
use GraphQL\Error\Error;
|
use GraphQL\Error\Error;
|
||||||
|
use GraphQL\Error\InvariantViolation;
|
||||||
use GraphQL\Language\AST\StringValueNode;
|
use GraphQL\Language\AST\StringValueNode;
|
||||||
use GraphQL\Type\Definition\ScalarType;
|
use GraphQL\Type\Definition\ScalarType;
|
||||||
use GraphQL\Utils\Utils;
|
use GraphQL\Utils\Utils;
|
||||||
@ -62,9 +63,12 @@ class EmailType extends ScalarType
|
|||||||
{
|
{
|
||||||
// Assuming internal representation of email is always correct:
|
// Assuming internal representation of email is always correct:
|
||||||
return $value;
|
return $value;
|
||||||
|
|
||||||
// If it might be incorrect and you want to make sure that only correct values are included in response -
|
// If it might be incorrect and you want to make sure that only correct values are included
|
||||||
// use following line instead:
|
// in response - use following line instead:
|
||||||
|
// if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||||
|
// throw new InvariantViolation("Could not serialize following value as email: " . Utils::printSafe($value));
|
||||||
|
// }
|
||||||
// return $this->parseValue($value);
|
// return $this->parseValue($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +81,7 @@ class EmailType extends ScalarType
|
|||||||
public function parseValue($value)
|
public function parseValue($value)
|
||||||
{
|
{
|
||||||
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||||
throw new \UnexpectedValueException("Cannot represent value as email: " . Utils::printSafe($value));
|
throw new Error("Cannot represent following value as email: " . Utils::printSafeJson($value));
|
||||||
}
|
}
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,11 @@ pages:
|
|||||||
- Schema: type-system/schema.md
|
- Schema: type-system/schema.md
|
||||||
- Using Type Language: type-system/type-language.md
|
- Using Type Language: type-system/type-language.md
|
||||||
- Executing Queries: executing-queries.md
|
- Executing Queries: executing-queries.md
|
||||||
- Fetching Data: data-fetching.md
|
- Fetching Data (resolving fields): data-fetching.md
|
||||||
- Handling Errors: error-handling.md
|
- Handling Errors: error-handling.md
|
||||||
# - Mutations: mutations.md
|
# - Mutations: mutations.md
|
||||||
# - Security: security.md
|
# - Security: security.md
|
||||||
# - Performance tips: performance.md
|
# - Performance tips: performance.md
|
||||||
# - Standard Server: server.md
|
|
||||||
- How it works: how-it-works.md
|
- How it works: how-it-works.md
|
||||||
- Class Reference: reference.md
|
- Class Reference: reference.md
|
||||||
- Complementary Tools: complementary-tools.md
|
- Complementary Tools: complementary-tools.md
|
||||||
|
Loading…
x
Reference in New Issue
Block a user