mirror of
https://github.com/retailcrm/graphql-php.git
synced 2024-11-22 04:46:04 +03:00
Work in progress on better docs
This commit is contained in:
parent
eb3f54b98e
commit
e69667c633
19
docs/best-practices.md
Normal file
19
docs/best-practices.md
Normal 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:
|
3
docs/complementary-tools.md
Normal file
3
docs/complementary-tools.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Relay
|
||||||
|
|
||||||
|
# GraphiQL
|
@ -1,14 +1,14 @@
|
|||||||
# Concepts
|
# Overview
|
||||||
GraphQL is data-centric. On the very top level it is built around three major concepts:
|
GraphQL is data-centric. On the very top level it is built around three major concepts:
|
||||||
**Schema**, **Query** and **Mutation**.
|
**Schema**, **Query** and **Mutation**.
|
||||||
|
|
||||||
You are expected to expresses your application as **Schema** (aka Type System) and expose it
|
You are expected to express your application as **Schema** (aka Type System) and expose it
|
||||||
with single HTTP endpoint. Application clients (e.g. web or mobile clients) send **Queries**
|
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.
|
to this endpoint to request structured data and **Mutations** to perform changes (usually with HTTP POST method).
|
||||||
|
|
||||||
## Queries
|
## Queries
|
||||||
Queries are expressed in simple language that resembles JSON:
|
Queries are expressed in simple language that resembles JSON:
|
||||||
|
|
||||||
```graphql
|
```graphql
|
||||||
{
|
{
|
||||||
hero {
|
hero {
|
||||||
@ -19,7 +19,7 @@ Queries are expressed in simple language that resembles JSON:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It was designed to mirror the structure of expected response:
|
It was designed to mirror the structure of expected response:
|
||||||
```json
|
```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
|
**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
|
||||||
Mutations use advanced features of the very same query language (like arguments and variables)
|
Mutations use advanced features of the very same query language (like arguments and variables)
|
||||||
and have only semantic difference from Queries:
|
and have only semantic difference from Queries:
|
||||||
|
|
||||||
```graphql
|
```graphql
|
||||||
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
|
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
|
||||||
createReview(episode: $ep, review: $review) {
|
createReview(episode: $ep, review: $review) {
|
||||||
@ -78,17 +80,59 @@ returned after mutation. In our example mutation will return:
|
|||||||
```
|
```
|
||||||
|
|
||||||
# Type System
|
# 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.
|
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:
|
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 **enumerations** and **unions**
|
||||||
* Primitives for defining custom **scalar types**
|
* Primitives for defining custom **scalar types**
|
||||||
* Built-in scalar types: `ID`, `String`, `Int`, `Float`, `Boolean`
|
* Built-in scalar types: `ID`, `String`, `Int`, `Float`, `Boolean`
|
||||||
* Built-in type modifiers: `ListOf` and `NonNull`
|
* Built-in type modifiers: `ListOf` and `NonNull`
|
||||||
|
|
||||||
# Further Reading
|
Same example expressed in **graphql-php**:
|
||||||
To get deeper understanding of GraphQL concepts - [read the docs on official website](http://graphql.org/learn/)
|
```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
0
docs/data-fetching.md
Normal 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
|
# Installation
|
||||||
|
|
||||||
Using [composer](https://getcomposer.org/doc/00-intro.md):
|
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": {
|
"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:
|
(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)
|
||||||
```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'];
|
|
||||||
}
|
|
||||||
],
|
|
||||||
],
|
|
||||||
]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The interesting piece here is `resolve` option of field definition. It is responsible for retuning
|
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**
|
value of our field. Values of **scalar** fields will be directly included in response while values of
|
||||||
values will be passed down to nested field resolvers (not in this example though).
|
**complex** fields (objects, interfaces, unions) 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.
|
|
||||||
|
|
||||||
Now when our type is ready, let's create GraphQL endpoint for it `graphql.php`:
|
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;
|
use GraphQL\Schema;
|
||||||
|
|
||||||
$schema = new Schema([
|
$schema = new Schema([
|
||||||
'query' => $queryType, // or new MyApp\Type\QueryType()
|
'query' => $queryType
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$rawInput = file_get_contents('php://input');
|
$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\") }"
|
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.
|
|
||||||
|
Obviously hello world only scratches the surface of what is possible.
|
||||||
So check out next example, which is closer to real-world apps.
|
So check out next example, which is closer to real-world apps.
|
||||||
|
Or keep reading about [schema definition](type-system/).
|
||||||
Or just keep reading about [type system](types/) definitions.
|
|
||||||
|
|
||||||
# Blog example
|
# Blog example
|
||||||
It is often easier to start with full-featured example and then get back to documentation
|
It is often easier to start with full-featured example and then get back to documentation
|
||||||
|
@ -13,9 +13,9 @@ All of them equally apply to this PHP implementation.
|
|||||||
|
|
||||||
# About graphql-php
|
# About graphql-php
|
||||||
|
|
||||||
**graphql-php** is an implementation of GraphQL specification in PHP (5.4+, 7.0+).
|
**graphql-php** is a feature-complete implementation of GraphQL specification in PHP (5.4+, 7.0+).
|
||||||
It is based on [JavaScript implementation](https://github.com/graphql/graphql-js)
|
It was originally inspired by [reference JavaScript implementation](https://github.com/graphql/graphql-js)
|
||||||
published by Facebook as a reference for others.
|
published by Facebook.
|
||||||
|
|
||||||
This library is a thin wrapper around your existing data layer and business logic.
|
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
|
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:
|
These tools include:
|
||||||
|
|
||||||
- Primitives to express your app as a Type System
|
- 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
|
- 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
|
Also several [complementary tools](complementary-tools/) are available which provide integrations with
|
||||||
```php
|
existing PHP frameworks, add support for Relay, etc.
|
||||||
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.
|
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
Current version supports all features described by GraphQL specification
|
Current version supports all features described by GraphQL specification
|
||||||
|
@ -1 +0,0 @@
|
|||||||
TODOC
|
|
36
docs/type-system/abstract-types.md
Normal file
36
docs/type-system/abstract-types.md
Normal 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
|
180
docs/type-system/enum-types.md
Normal file
180
docs/type-system/enum-types.md
Normal 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
158
docs/type-system/index.md
Normal 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.
|
61
docs/type-system/lists-and-nonnulls.md
Normal file
61
docs/type-system/lists-and-nonnulls.md
Normal 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.
|
0
docs/type-system/non-null.md
Normal file
0
docs/type-system/non-null.md
Normal file
188
docs/type-system/object-types.md
Normal file
188
docs/type-system/object-types.md
Normal 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.
|
148
docs/type-system/scalar-types.md
Normal file
148
docs/type-system/scalar-types.md
Normal 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 */},
|
||||||
|
]);
|
||||||
|
```
|
0
docs/type-system/schema.md
Normal file
0
docs/type-system/schema.md
Normal file
13
mkdocs.yml
13
mkdocs.yml
@ -1,7 +1,16 @@
|
|||||||
site_name: graphql-php
|
site_name: graphql-php
|
||||||
pages:
|
pages:
|
||||||
- About: index.md
|
- About: index.md
|
||||||
- Overview: overview.md
|
|
||||||
- Getting Started: getting-started.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
|
theme: readthedocs
|
||||||
|
Loading…
Reference in New Issue
Block a user