Further documentation improvements

This commit is contained in:
Vladimir Razuvaev 2017-08-18 02:56:22 +07:00
parent 2537a62ec2
commit 637156fe65
7 changed files with 167 additions and 73 deletions

View File

@ -1,6 +1,6 @@
## 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.
### 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
See https://github.com/webonyx/graphql-php/issues/35
### Deprecated: `GraphQL\GraphQL::executeAndReturnResult` renamed to `GraphQL\GraphQL::executeQuery`
Old method name is still available, but will trigger deprecation warning in next version.
### Deprecated: `GraphQL\GraphQL::executeAndReturnResult`
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`
Use `GraphQL\GraphQL::executeQuery()->toArray()` instead.
@ -95,6 +96,13 @@ $schema->assertValid();
```
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
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.

View File

@ -3,10 +3,10 @@ GraphQL is data-storage agnostic. You can use any underlying data storage engine
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
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).
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).
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
$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
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
$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
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.
# 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:
```php
GraphQL\GraphQL::setDefaultFieldResolver($myResolverCallback);
```
To override the default resolver, pass it as an argument of [executeQuery](executing-queries) call.
# Default Field Resolver per Type
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
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:
```
{
@ -219,28 +217,41 @@ In this example only one query will be executed for all story authors comparing
in naive implementation.
# 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
(like `HHVM`, `ReactPHP`, `Icicle.io`, `appserver.io` `PHP threads`, etc) you can leverage
the power of your platform to resolve fields asynchronously.
(like HHVM, ReactPHP, Icicle.io, appserver.io, PHP threads, etc)
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
[Promises A+](https://promisesaplus.com/) specification.
To enable async support - set adapter for promises:
```
GraphQL\GraphQL::setPromiseAdapter($adapter);
To start using this feature, switch facade method for query execution from
**executeQuery** to **promiseToExecute**:
```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
`GraphQL\Deferred` instances.
* For [ReactPHP](https://github.com/reactphp/react) (requires **react/promise** as composer dependency): <br>
`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):
`GraphQL\GraphQL::setPromiseAdapter(new GraphQL\Executor\Promise\Adapter\ReactPromiseAdapter());`
To integrate other platform - implement `GraphQL\Executor\Promise\PromiseAdapter` interface.
Then your **resolve** functions should return promises of your platform instead of `GraphQL\Deferred`s.

View File

@ -48,4 +48,7 @@ Schema Language parser.
Ready for real-world usage.
## 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/).

View File

@ -21,8 +21,7 @@ class GraphQL
$variableValues = null,
$operationName = null,
callable $fieldResolver = null,
array $validationRules = null,
GraphQL\Executor\Promise\PromiseAdapter $promiseAdapter = null
array $validationRules = null
);
/**
@ -502,22 +501,20 @@ use GraphQL\Executor\Promise\PromiseAdapter;
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.
* By default simply returns ExecutionResult
*
* @return ExecutionResult|Promise
* @return Promise
*/
public static function execute(
public static function promiseToExecute(
PromiseAdapter $promiseAdapter,
Schema $schema,
DocumentNode $ast,
$rootValue = null,
$contextValue = null,
$variableValues = null,
$operationName = null,
callable $fieldResolver = null,
PromiseAdapter $promiseAdapter = null
callable $fieldResolver = 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
```php
@ -910,3 +981,9 @@ class FormattedError
public static function prepareFormatter(callable $formatter = null, $debug);
}
```
# GraphQL\Server\OperationParams
# GraphQL\Server\StandardServer
# GraphQL\Server\Helper

View File

@ -50,34 +50,26 @@ class MyType extends ObjectType
}
```
You can also mix-and-match styles for convenience. For example:
```php
<?php
namespace MyApp;
Using [GraphQL Type language](graphql.org/learn/schema/#type-language):
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
```graphql
schema {
query: Query
mutation: Mutation
}
class BlogPostType extends ObjectType
{
public function __construct()
{
$config = [
'fields' => [
'body' => new ObjectType([
'name' => 'BlogPostBody',
'fields' => [
'html' => Type::string(),
'text' => Type::string(),
]
])
]
];
parent::__construct($config);
}
type Query {
greetings(input: HelloInput!): String!
}
input HelloInput {
firstName: String!
lastName: String
}
```
[Read more](/type-system/type-language/) about it in a dedicated docs section.
# Type Registry
Every type must be presented in Schema by single instance (**graphql-php**
throws when it discovers several instances with the same `name` in schema).

View File

@ -42,6 +42,7 @@ Here is an example of simple `Email` type:
namespace MyApp;
use GraphQL\Error\Error;
use GraphQL\Error\InvariantViolation;
use GraphQL\Language\AST\StringValueNode;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Utils\Utils;
@ -62,9 +63,12 @@ class EmailType extends ScalarType
{
// Assuming internal representation of email is always correct:
return $value;
// If it might be incorrect and you want to make sure that only correct values are included in response -
// use following line instead:
// If it might be incorrect and you want to make sure that only correct values are included
// 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);
}
@ -77,7 +81,7 @@ class EmailType extends ScalarType
public function parseValue($value)
{
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;
}

View File

@ -15,12 +15,11 @@ pages:
- Schema: type-system/schema.md
- Using Type Language: type-system/type-language.md
- Executing Queries: executing-queries.md
- Fetching Data: data-fetching.md
- Fetching Data (resolving fields): data-fetching.md
- Handling Errors: error-handling.md
# - Mutations: mutations.md
# - Security: security.md
# - Performance tips: performance.md
# - Standard Server: server.md
- How it works: how-it-works.md
- Class Reference: reference.md
- Complementary Tools: complementary-tools.md