Work in progress on better docs

This commit is contained in:
vladar 2016-11-05 23:55:51 +07:00
parent eb3f54b98e
commit e69667c633
16 changed files with 884 additions and 140 deletions

19
docs/best-practices.md Normal file
View File

@ -0,0 +1,19 @@
# Config Validation
Defining types using arrays may be error-prone, but **graphql-php** provides config validation
tool to report when config has unexpected structure.
This validation tool is **disabled by default** because it is time-consuming operation which only
makes sense during development.
To enable validation - call: `GraphQL\Type\Definition\Config::enableValidation();` in your bootstrap
but make sure to restrict it to debug/development mode only.
# Type Registry
**graphql-php** expects that each type in Schema is presented with single instance. Therefore
if you define your types as separate PHP classes you need to ensure that each type is referenced only once.
Technically you can create several instances of your type (for example for tests), but `GraphQL\Schema`
will throw on attempt to add different instances with the same name.
There are several ways to achieve this depending on your preferences. We provide reference
implementation below that introduces TypeRegistry class:

View File

@ -0,0 +1,3 @@
# Relay
# GraphiQL

View File

@ -1,10 +1,10 @@
# Concepts
# Overview
GraphQL is data-centric. On the very top level it is built around three major concepts:
**Schema**, **Query** and **Mutation**.
You are expected to expresses your application as **Schema** (aka Type System) and expose it
with single HTTP endpoint. Application clients (e.g. web or mobile clients) send **Queries**
to this endpoint to request structured data and **Mutations** to perform changes.
You are expected to express your application as **Schema** (aka Type System) and expose it
with single [HTTP endpoint](http-endpoint/). Application clients (e.g. web or mobile clients) send **Queries**
to this endpoint to request structured data and **Mutations** to perform changes (usually with HTTP POST method).
## Queries
Queries are expressed in simple language that resembles JSON:
@ -34,11 +34,13 @@ It was designed to mirror the structure of expected response:
}
```
**graphql-php** runtime parses Queries, makes sure that they are valid for given Type System
and executes using data resolving tools provided by you as a part of integration.
and executes using [data fetching tools](type-system/object-types/#data-fetching) provided by you
as a part of integration. Queries are supposed to be idempotent.
## Mutations
Mutations use advanced features of the very same query language (like arguments and variables)
and have only semantic difference from Queries:
```graphql
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
@ -78,17 +80,59 @@ returned after mutation. In our example mutation will return:
```
# Type System
Conceptually GraphQL type is a collection of fields. Each field in turn
has it's own type which allows to build complex hierarchies.
Quick example on pseudo-language:
```
type BlogPost {
title: String!
author: User
body: String
}
type User {
id: Id!
firstName: String
lastName: String
}
```
Type system is a heart of GraphQL integration. That's where **graphql-php** comes into play.
It provides following tools and primitives to describe your App as hierarchy of types:
* Primitives to work with **objects** and **interfaces**
* Primitives for defining **objects** and **interfaces**
* Primitives for defining **enumerations** and **unions**
* Primitives for defining custom **scalar types**
* Built-in scalar types: `ID`, `String`, `Int`, `Float`, `Boolean`
* Built-in type modifiers: `ListOf` and `NonNull`
# Further Reading
To get deeper understanding of GraphQL concepts - [read the docs on official website](http://graphql.org/learn/)
Same example expressed in **graphql-php**:
```php
<?php
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
To get started with your own app - continue to next section ["Getting Started"](getting-started/)
$userType = new ObjectType([
'name' => 'User',
'fields' => [
'id' => Type::nonNull(Type::id()),
'firstName' => Type::string(),
'lastName' => Type::string()
]
]);
$blogPostType = new ObjectType([
'name' => 'BlogPost',
'fields' => [
'title' => Type::nonNull(Type::string()),
'author' => $userType
]
]);
```
# Further Reading
To get deeper understanding of GraphQL concepts - [read the docs on official GraphQL website](http://graphql.org/learn/)
To get started with **graphql-php** - continue to next section ["Getting Started"](getting-started/)

0
docs/data-fetching.md Normal file
View File

View File

@ -1,3 +1,7 @@
# Prerequisites
This documentation assumes your familiarity with GraphQL concepts. If it is not the case -
first learn about GraphQL on [official website](http://graphql.org/learn/).
# Installation
Using [composer](https://getcomposer.org/doc/00-intro.md):
@ -5,7 +9,7 @@ add `composer.json` file to your project root folder with following contents:
```
{
"require": {
"webonyx/graphql-php": "^0.8"
"webonyx/graphql-php": "^0.7"
}
}
```
@ -63,77 +67,13 @@ $queryType = new ObjectType([
]);
```
Same could be written as separate class:
```php
<?php
namespace MyApp\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
class QueryType extends ObjectType
{
public function __construct()
{
$config = [
// Note: name is not required in this form, as it will be inferred
// from className ("Type" suffix will be dropped)
'fields' => [
'echo' => [
'type' => Type::string(),
'args' => [
'message' => Type::nonNull(Type::string()),
],
'resolve' => function ($root, $args) {
return $root['prefix'] . $args['message'];
}
],
],
];
parent::__construct($config);
}
}
```
Or for those who prefer composition over inheritance:
```php
<?php
namespace MyApp\Type;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\DefinitionContainer;
class QueryType implements DefinitionContainer
{
private $definition;
public function getDefinition()
{
return $this->definition ?: ($this->definition = new \GraphQL\Type\Definition\ObjectType([
'name' => 'Query',
'fields' => [
'echo' => [
'type' => Type::string(),
'args' => [
'message' => Type::nonNull(Type::string()),
],
'resolve' => function ($root, $args) {
return $root['prefix'] . $args['message'];
}
],
],
]));
}
}
```
(Note: type definition can be expressed in [different styles](type-system/#type-definition-styles),
including **inheritance**, **composition** and **inline**. This example uses **inline** style for simplicity)
The interesting piece here is `resolve` option of field definition. It is responsible for retuning
value for our field. **Scalar** values will be directly included in response while **complex object**
values will be passed down to nested field resolvers (not in this example though).
Field resolvers is the main mechanism of **graphql-php** to bind type system with your
underlying data source.
value of our field. Values of **scalar** fields will be directly included in response while values of
**complex** fields (objects, interfaces, unions) will be passed down to nested field resolvers
(not in this example though).
Now when our type is ready, let's create GraphQL endpoint for it `graphql.php`:
@ -143,7 +83,7 @@ use GraphQL\GraphQL;
use GraphQL\Schema;
$schema = new Schema([
'query' => $queryType, // or new MyApp\Type\QueryType()
'query' => $queryType
]);
$rawInput = file_get_contents('php://input');
@ -168,11 +108,11 @@ php -S localhost:8000 graphql.php
curl http://localhost:8000 -d "query { echo(message: \"Hello World\") }"
```
Or grab the full [source code](https://github.com/webonyx/graphql-php/blob/master/examples/00-hello-world).
Check out the full [source code](https://github.com/webonyx/graphql-php/blob/master/examples/00-hello-world) of this example.
Obviously hello world only scratches the surface of what is possible.
So check out next example, which is closer to real-world apps.
Or just keep reading about [type system](types/) definitions.
Or keep reading about [schema definition](type-system/).
# Blog example
It is often easier to start with full-featured example and then get back to documentation

View File

@ -13,9 +13,9 @@ All of them equally apply to this PHP implementation.
# About graphql-php
**graphql-php** is an implementation of GraphQL specification in PHP (5.4+, 7.0+).
It is based on [JavaScript implementation](https://github.com/graphql/graphql-js)
published by Facebook as a reference for others.
**graphql-php** is a feature-complete implementation of GraphQL specification in PHP (5.4+, 7.0+).
It was originally inspired by [reference JavaScript implementation](https://github.com/graphql/graphql-js)
published by Facebook.
This library is a thin wrapper around your existing data layer and business logic.
It doesn't dictate how these layers are implemented or which storage engines
@ -24,54 +24,13 @@ are used. Instead it provides tools for creating rich API for your existing app.
These tools include:
- Primitives to express your app as a Type System
- Tools for validation and introspection of this Type System
- Tools for validation and introspection of this Type System (for compatibility with tools like [GraphiQL](complementary-tools/#graphiql))
- Tools for parsing, validating and executing GraphQL queries against this Type System
- Rich error reporting
- Rich error reporting, including query validation and execution errors
- Optional tools for parsing GraphQL Schema Definition language
## Usage Example
```php
use GraphQL\GraphQL;
use GraphQL\Schema;
$query = '
{
hero {
id
name
friends {
name
}
}
}
';
$schema = new Schema([
// ...
// Type System definition for your app goes here
// ...
]);
$result = GraphQL::execute($schema, $query);
```
Result returned:
```php
[
'hero' => [
'id' => '2001',
'name' => 'R2-D2',
'friends' => [
['name' => 'Luke Skywalker'],
['name' => 'Han Solo'],
['name' => 'Leia Organa'],
]
]
]
```
Also check out full [Type System](https://github.com/webonyx/graphql-php/blob/master/tests/StarWarsSchema.php)
and [data source](https://github.com/webonyx/graphql-php/blob/master/tests/StarWarsData.php)
of this example.
Also several [complementary tools](complementary-tools/) are available which provide integrations with
existing PHP frameworks, add support for Relay, etc.
## Current Status
Current version supports all features described by GraphQL specification

View File

@ -1 +0,0 @@
TODOC

View File

@ -0,0 +1,36 @@
# Interfaces
In GraphQL an Interface is an abstract type that includes a certain set of fields that a
type must include to implement the interface.
In **graphql-php** interface type is an instance of `GraphQL\Type\Definition\InterfaceType`
(or one of it subclasses) which accepts configuration array in constructor:
```php
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\Type;
$characterInterface = new InterfaceType([
'name' => 'Character',
'description' => 'A character in the Star Wars Trilogy',
'fields' => [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the character.',
],
'name' => [
'type' => Type::string(),
'description' => 'The name of the character.'
]
],
'resolveType' => function ($obj) {
if ($obj->type === 'human') {
return MyTypes::human();
} else {
return MyTypes::droid();
}
return null;
}
]);
```
# Abstract Type Resolution

View File

@ -0,0 +1,180 @@
# Enum Type Definition
Enumeration types are a special kind of scalar that is restricted to a particular set
of allowed values.
In **graphql-php** enum type is an instance of `GraphQL\Type\Definition\EnumType`
(or one of it subclasses) which accepts configuration array in constructor:
```php
use GraphQL\Type\Definition\EnumType;
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => [
'NEWHOPE' => [
'value' => 4,
'description' => 'Released in 1977.'
],
'EMPIRE' => [
'value' => 5,
'description' => 'Released in 1980.'
],
'JEDI' => [
'value' => 6,
'description' => 'Released in 1983.'
],
]
]);
```
This example uses **inline** style for Enum Type definition, but there are also
[other styles](/type-system/#type-definition-styles) (using inheritance or composition).
# Configuration options
Enum Type constructor accepts array with following options:
Option | Type | Notes
------ | ---- | -----
name | `string` | **Required.** Name of the type. When not set - inferred from array key (read about [shorthand field definition](#) below)
description | `string` | Plain-text description of the type for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation)
values | `array` | List of enumerated items, see below for expected structure of each entry
Each entry of **values** array in turn accepts following options:
Option | Type | Notes
------ | ---- | -----
name | `string` | **Required.** Name of the item. When not set - inferred from array key (read about [shorthand field definition](#) below)
value | `mixed` | Internal representation of enum item in your application (could be any value, including complex objects or callbacks)
description | `string` | Plain-text description of enum value for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation)
deprecationReason | `string` | Text describing why this enum value is deprecated. When not empty - item will not be returned by introspection queries (unless forced)
# Shorthand definitions
If internal representation of enumerated item is the same as item name, then you can use
following shorthand for definition:
```php
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => ['NEWHOPE', 'EMPIRE', 'JEDI']
]);
```
which is equivalent of:
```php
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => [
'NEWHOPE' => 'NEWHOPE',
'EMPIRE' => 'EMPIRE',
'JEDI' => 'JEDI'
]
]);
```
which is in turn equivalent of:
```php
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => [
'NEWHOPE' => ['value' => 'NEWHOPE'],
'EMPIRE' => ['value' => 'EMPIRE'],
'JEDI' => ['value' => 'JEDI']
]
]);
```
which is in turn equivalent of full form:
```php
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => [
['name' => 'NEWHOPE', 'value' => 'NEWHOPE'],
['name' => 'EMPIRE', 'value' => 'EMPIRE'],
['name' => 'JEDI', 'value' => 'JEDI']
]
]);
```
# Field Resolution
When object field is of Enum Type, field resolver is expected to return internal
representation of corresponding Enum item (**value** in config). **graphql-php** will
then serialize this **value** to **name** to include in response:
```php
use GraphQL\Type\Definition\EnumType;
use GraphQL\Type\Definition\ObjectType;
$episodeEnum = new EnumType([
'name' => 'Episode',
'description' => 'One of the films in the Star Wars Trilogy',
'values' => [
'NEWHOPE' => [
'value' => 4,
'description' => 'Released in 1977.'
],
'EMPIRE' => [
'value' => 5,
'description' => 'Released in 1980.'
],
'JEDI' => [
'value' => 6,
'description' => 'Released in 1983.'
],
]
]);
$heroType = new ObjectType([
'name' => 'Hero',
'fields' => [
'appearsIn' => [
'type' => $episodeEnum,
'resolve' => function() {
return 5; // Actual entry in response will be 'appearsIn' => 'EMPIRE'
}
]
]
])
```
Reverse is true when enum is used as input type (e.g. as field argument).
GraphQL will treat enum input as **name** and convert it into **value** before passing to your app.
For example, given object type definition:
```php
$heroType = new ObjectType([
'name' => 'Hero',
'fields' => [
'appearsIn' => [
'type' => Type::boolean(),
'args' => [
'episode' => Type::nonNull($enumType)
]
'resolve' => function($_value, $args) {
return $args['episode'] === 5 ? true : false;
}
]
]
])
```
Then following query:
```
fragment on Hero {
appearsInNewHope: appearsIn(NEWHOPE)
appearsInEmpire: appearsIn(EMPIRE)
}
```
will return:
```php
[
'appearsInNewHope' => false,
'appearsInEmpire' => true
]
```

158
docs/type-system/index.md Normal file
View File

@ -0,0 +1,158 @@
# Type System
To start using GraphQL you are expected to implement a Type system.
In **graphql-php** `type` is an instance of internal class from
`GraphQL\Type\Definition` namespace: `ScalarType`, `ObjectType`, `InterfaceType`,
`UnionType`, `InputObjectType` (or one of it's subclasses).
But most of the types in your schema will be [object types](object-types/).
# Type Definition Styles
Several styles of type definitions are supported depending on your preferences.
Inline definitions:
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
$myType = new ObjectType([
'name' => 'MyType',
'fields' => [
'id' => Type::id()
]
]);
```
Class per type, using inheritance:
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
class MyType extends ObjectType
{
public function __construct()
{
$config = [
// Note: 'name' is not needed in this form:
// it will be inferred from class name by omitting namespace and dropping "Type" suffix
'fields' => [
'id' => Type::id()
]
];
parent::__construct($config);
}
}
```
Class per type, using composition:
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\DefinitionContainer;
class MyType implements DefinitionContainer
{
private $definition;
public function getDefinition()
{
return $this->definition ?: ($this->definition = new ObjectType([
'name' => 'MyType',
'fields' => [
'id' => Type::id()
]
]));
}
}
```
You can also mix-and-match styles for convenience. For example:
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
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 Registry
Every type must be presented in Schema with single instance (**graphql-php**
throws when it discovers several instances with the same `name` in schema).
Therefore if you define your type as separate PHP class you must ensure that only one
instance of that class is added to schema.
Typical way to do this is to create registry of your types:
```php
<?php
namespace MyApp;
class TypeRegistry
{
private $myAType;
private $myBType;
public function myAType()
{
return $this->myAType ?: ($this->myAType = new MyAType($this));
}
public function myBType()
{
return $this->myBType ?: ($this->myBType = new MyBType($this));
}
}
```
And use this registry in type definition:
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\ObjectType;
class MyAType extends ObjectType
{
public function __construct(TypeRegistry $types)
{
parent::__construct([
'fields' => [
'b' => $types->myBType()
]
]);
}
}
```
Obviously you can automate this registry as you wish to reduce boilerplate or even
introduce Dependency Injection Container if your types have other dependencies.
Alternatively all methods of registry could be static if you prefer - then there is no need
to pass it in constructor - instead just use use `MyTypes::myAType()` in your type definitions.

View File

@ -0,0 +1,61 @@
# Lists
**graphql-php** provides built-in support for lists. In order to create list type - wrap
existing type with `GraphQL\Type\Definition\Type::listOf()` modifier:
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
$userType = new ObjectType([
'name' => 'User',
'fields' => [
'emails' => [
'type' => Type::listOf(Type::string()),
'resolve' => function() {
return ['jon@example.com', 'jonny@example.com'];
}
]
]
]);
```
Resolvers for such fields are expected to return `array` or instance of `Traversable` interface
(`null` is allowed by default too).
If returned value is not of one of these types - **graphql-php** will add an error to result
and set field value to `null` (only if field is nullable, see below for non-null fields).
# Non-Null types
By default in GraphQL every field can have `null` value. To indicate that some field always
returns `non-null` value - use `GraphQL\Type\Definition\Type::nonNull()` modifier:
```php
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
$humanType = new ObjectType([
'name' => 'User',
'fields' => [
'id' => [
'type' => Type::nonNull(Type::id()),
'resolve' => function() {
return uniqid();
}
],
'emails' => [
'type' => Type::nonNull(Type::listOf(Type::string())),
'resolve' => function() {
return ['jon@example.com', 'jonny@example.com'];
}
]
]
]);
```
If resolver of non-null field returns `null`, **graphql-php** will add an error to
result and exclude whole object from output (error will bubble to first nullable parent
field which will be set to `null`).
Read section on [Data Fetching](#) for details.

View File

View File

@ -0,0 +1,188 @@
# Object Type Definition
Object Type is the most frequently used primitive in typical GraphQL application.
Conceptually Object Type is a collection of Fields. Each field in turn
has it's own type which allows to build complex hierarchies.
In **graphql-php** object type is an instance of `GraphQL\Type\Definition\ObjectType`
(or one of it subclasses) which accepts configuration array in constructor:
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Examples\Blog\Data\DataSource;
use GraphQL\Examples\Blog\Data\Story;
$userType = new ObjectType([
'name' => 'User',
'description' => 'Our blog visitor',
'fields' => [
'firstName' => [
'type' => Type::string(),
'description' => 'User first name'
],
'email' => Type::string()
]
]);
$blogStory = new ObjectType([
'name' => 'Story',
'fields' => [
'body' => Type::string(),
'author' => [
'type' => $userType,
'description' => 'Story author',
'resolve' => function(Story $blogStory) {
return DataSource::findUser($blogStory->authorId);
}
],
'likes' => [
'type' => Type::listOf($userType),
'description' => 'List of users who liked the story',
'args' => [
'limit' => [
'type' => Type::int(),
'description' => 'Limit the number of recent likes returned',
'defaultValue' => 10
]
],
'resolve' => function(Story $blogStory, $args) {
return DataSource::findLikes($blogStory->id, $args['limit']);
}
]
]
]);
```
This example uses **inline** style for Object Type definitions, but there are also
[other styles](/type-system/#type-definition-styles) (using inheritance or composition).
# Configuration options
Object type constructor expects configuration array. Below is a full list of available options:
Option | Type | Notes
------------ | -------- | -----
name | `string` | **Required.** Unique name of this object type within Schema
fields | `array` or `callback` returning `array` | **Required**. Array describing object fields. See [Fields](#field-definitions) section below for expected structure of each array entry. See also section on [Circular types](#) for explanation of when to use callback for this option.
description | `string` | Plain-text description of this type for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation)
interfaces | `array` or `callback` returning `array` | List of interfaces implemented by this type. See [Interface Types](/type-system/interface-types) for details. See also section on [Circular types](#) for explanation of when to use callback for this option.
isTypeOf | `callback` returning `boolean` | **function($value, $context, GraphQL\Type\Definition\ResolveInfo $info)** Expected to return `true` if `$value` qualifies for this type (see section about [Abstract Type Resolution](#) for explanation).
resolveField | `callback` returning `mixed` | **function($value, $args, $context, GraphQL\Type\Definition\ResolveInfo $info)** Given the `$value` of this type it is expected to return value for field defined in `$info->fieldName`. Good place to define type-specific strategy for field resolution. See section on [Data Fetching](#) for details.
# Field configuration options
Below is a full list of available field configuration options:
Option | Type | Notes
------ | ---- | -----
name | `string` | **Required.** Name of the field. When not set - inferred from array key (read about [shorthand field definition](#) below)
type | `Type` | **Required.** Instance of internal or custom type. Note: type must be represented by single instance within schema (see also [Type Registry](#))
args | `array` | Array of possible type arguments. Each entry is expected to be an array with keys: **name**, **type**, **description**, **defaultValue**
resolve | `callback` | **function($value, $args, $context, GraphQL\Type\Definition\ResolveInfo $info)** Given the `$value` of this type it is expected to return value for current field. See section on [Data Fetching](#) for details
description | `string` | Plain-text description of this field description for clients (e.g. used by [GraphiQL](https://github.com/graphql/graphiql) for auto-generated documentation)
deprecationReason | `string` | Text describing why this field is deprecated. When not empty - field will not be returned by introspection queries (unless forced)
# Shorthand field definitions
Fields can be also defined in **shorthand** notation (without `description`, `args` and `defaultValue`):
```php
'fields' => [
'id' => Type::id(),
'fieldName' => $fieldType
]
```
which is equivalent of:
```php
'fields' => [
'id' => ['type' => Type::id()],
'fieldName' => ['type' => $fieldName]
]
```
which is in turn equivalent of full form:
```php
'fields' => [
['name' => 'id', 'type' => Type::id()],
['name' => 'fieldName', 'type' => $fieldName]
]
```
Same shorthand notation applies to field arguments as well.
# Recurring and circular types
Almost all real-world applications contain recurring or circular types.
Think user friends or nested comments for example.
**graphql-php** allows such types, but you have to use `callback` in
option **fields** (and/or **interfaces**).
For example:
```php
$userType = null;
$userType = new ObjectType([
'name' => 'User',
'fields' => function() use (&$userType) {
return [
'email' => [
'type' => Type::string()
],
'friends' => [
'type' => Type::listOf($userType)
]
];
}
]);
```
Same example for [inheritance style of type definitions](#) using [TypeRegistry](#):
```php
<?php
namespace MyApp;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
class UserType extends ObjectType
{
public function __construct()
{
$config = [
'fields' => function() {
return [
'email' => MyTypes::string(),
'friends' => MyTypes::listOf(MyTypes::user())
];
}
];
parent::__construct($config);
}
}
class MyTypes
{
private static $user;
public static function user()
{
return self::$user ?: (self::$user = new UserType());
}
public static function string()
{
return Type::string();
}
public static function listOf($type)
{
return Type::listOf($type);
}
}
```
# Field Resolution
Field resolution is the primary mechanism in **graphql-php** for returning actual data for your fields.
It is implemented using `resolveField` callback in type definition or `resolve`
callback in field definition (which has precedence).
Read section on [Data Fetching]() for complete description of this process.

View File

@ -0,0 +1,148 @@
# Built-in Scalar Types
GraphQL specification describes several built-in scalar types. In **graphql-php** they are
exposed as static methods of `GraphQL\Type\Definition\Type` class:
```php
use GraphQL\Type\Definition\Type;
// Built-in Scalar types:
Type::string(); // String type
Type::int(); // Int type
Type::float(); // Float type
Type::boolean(); // Boolean type
Type::id(); // ID type
```
Those methods return instances of `GraphQL\Type\Definition\ScalarType` (actually one of it subclasses).
Use them directly in type definitions, or wrap in your [TypeRegistry](/type-system/#type-registry)
(if you use one).
# Writing Custom Scalar Types
In addition to built-in scalars, you can define your own scalar types with additional validation.
Typical examples of such types are: `Email`, `Date`, `Url`, etc.
In order to implement your own type you must understand how scalars are presented in GraphQL.
GraphQL deals with scalars in following cases:
1. When converting **internal representation** of value returned by your app (e.g. stored in database
or hardcoded in source code) to **serialized** representation included in response.
2. When converting **input value** passed by client in variables along with GraphQL query to
**internal representation** of your app.
3. When converting **input literal value** hardcoded in GraphQL query (e.g. field argument value) to
**internal representation** of your app.
Those cases are covered by methods `serialize`, `parseValue` and `parseLiteral` of abstract `ScalarType`
class respectively.
Here is an example of simple `Email` type (using inheritance):
```php
<?php
namespace MyApp;
use GraphQL\Error\Error;
use GraphQL\Language\AST\StringValue;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Utils;
class EmailType extends ScalarType
{
// Note: name can be omitted. In this case it will be inferred from class name
// (suffix "Type" will be dropped)
public $name = 'Email';
/**
* Serializes an internal value to include in a response.
*
* @param string $value
* @return string
*/
public function serialize($value)
{
// 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:
// return $this->parseValue($value);
}
/**
* Parses an externally provided value (query variable) to use as an input
*
* @param mixed $value
* @return mixed
*/
public function parseValue($value)
{
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \UnexpectedValueException("Cannot represent value as email: " . Utils::printSafe($value));
}
return $value;
}
/**
* Parses an externally provided literal value (hardcoded in GraphQL query) to use as an input.
*
* E.g.
* {
* user(email: "user@example.com")
* }
*
* @param \GraphQL\Language\AST\Node $valueAST
* @return string
* @throws Error
*/
public function parseLiteral($valueAST)
{
// Note: throwing GraphQL\Error\Error vs \UnexpectedValueException to benefit from GraphQL
// error location in query:
if (!$valueAST instanceof StringValue) {
throw new Error('Query error: Can only parse strings got: ' . $valueAST->kind, [$valueAST]);
}
if (!filter_var($valueAST->value, FILTER_VALIDATE_EMAIL)) {
throw new Error("Not a valid email", [$valueAST]);
}
return $valueAST->value;
}
}
```
Same example, using composition over inheritance:
```php
<?php
namespace MyApp;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Type\Definition\CustomScalarType;
class EmailType implements DefinitionContainer
{
private $definition;
public function getDefinition()
{
return $this->definition ?: ($this->definition = new CustomScalarType([
'name' => 'Email',
'serialize' => function($value) {/* See function body above */},
'parseValue' => function($value) {/* See function body above */},
'parseLiteral' => function($valueAST) {/* See function body above */},
]));
}
}
```
Or with inline style:
```php
<?php
use GraphQL\Type\Definition\CustomScalarType;
$emailType = new CustomScalarType([
'name' => 'Email',
'serialize' => function($value) {/* See function body above */},
'parseValue' => function($value) {/* See function body above */},
'parseLiteral' => function($valueAST) {/* See function body above */},
]);
```

View File

View File

@ -1,7 +1,16 @@
site_name: graphql-php
pages:
- About: index.md
- Overview: overview.md
- Getting Started: getting-started.md
- Type System: type-system.md
- Schema Definition:
- Introduction: type-system/index.md
- Object Types: type-system/object-types.md
- Scalar Types: type-system/scalar-types.md
- Enumeration Types: type-system/enum-types.md
- Lists and Non-Null: type-system/lists-and-nonnulls.md
- Interfaces and Unions: type-system/abstract-types.md
- Defining Schema: type-system/schema.md
- Data Fetching: data-fetching.md
- Best Practices: best-practices.md
- Complementary Tools: complementary-tools.md
theme: readthedocs