• This repository has been archived on 08/Mar/2023
  • Stars
    star
    217
  • Rank 182,446 (Top 4 %)
  • Language
    PHP
  • License
    MIT License
  • Created almost 7 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A PHP7 implementation of the GraphQL specification.

GraphQL

GitHub Actions status Coverage Status Scrutinizer Code Quality License Backers on Open Collective Sponsors on Open Collective

This is a PHP implementation of the GraphQL specification based on the JavaScript reference implementation.

Related projects

Requirements

  • PHP version >= 7.1
  • ext-mbstring

Table of contents

Installation

Run the following command to install the package through Composer:

composer require digiaonline/graphql

Example

Here is a simple example that demonstrates how to build an executable schema from a GraphQL schema file that contains the Schema Definition Language (SDL) for a Star Wars-themed schema (for the schema definition itself, see below). In this example we use that SDL to build an executable schema and use it to query for the name of the hero. The result of that query is an associative array with a structure that resembles the query we ran.

use Digia\GraphQL\Language\FileSourceBuilder;
use function Digia\GraphQL\buildSchema;
use function Digia\GraphQL\graphql;

$sourceBuilder = new FileSourceBuilder(__DIR__ . '/star-wars.graphqls');

$schema = buildSchema($sourceBuilder->build(), [
    'Query' => [
        'hero' => function ($rootValue, $arguments) {
            return getHero($arguments['episode'] ?? null);
        },
    ],
]);

$result = graphql($schema, '
query HeroNameQuery {
  hero {
    name
  }
}');

\print_r($result);

The script above produces the following output:

Array
(
    [data] => Array
    (
        [hero] => Array
        (
            [name] => "R2-D2"
        )
        
    )
    
)

The GraphQL schema file used in this example contains the following:

schema {
    query: Query
}

type Query {
    hero(episode: Episode): Character
    human(id: String!): Human
    droid(id: String!): Droid
}

interface Character {
    id: String!
    name: String
    friends: [Character]
    appearsIn: [Episode]
}

type Human implements Character {
    id: String!
    name: String
    friends: [Character]
    appearsIn: [Episode]
    homePlanet: String
}

type Droid implements Character {
    id: String!
    name: String
    friends: [Character]
    appearsIn: [Episode]
    primaryFunction: String
}

enum Episode { NEWHOPE, EMPIRE, JEDI }

Creating a schema

In order to execute queries against your GraphQL API, you first need to define the structure of your API. This is done by creating a schema. There are two ways to do this, you can either do it using SDL or you can do it programmatically. However, we strongly encourage you to use SDL, because it is easier to work with. To make an executable schema from SDL you need to call the buildSchema function.

The buildSchema function takes three arguments:

  • $source The schema definition (SDL) as a Source instance
  • $resolverRegistry An associative array or a ResolverRegistry instance that contains all resolvers
  • $options The options for building the schema, which also includes custom types and directives

To create the Source instance you can use the provided FileSourceBuilder or MultiFileSourceBuilder classes.

Resolver registry

The resolver registry is essentially a flat map with the type names as its keys and their corresponding resolver instances as its values. For smaller projects you can use an associative array and lambda functions to define your resolver registry. However, in larger projects we suggest that you implement your own resolvers instead. You can read more about resolvers under the Resolvers section.

Associative array example:

$schema = buildSchema($source, [
    'Query' => [
        'hero' => function ($rootValue, $arguments) {
            return getHero($arguments['episode'] ?? null);
        },
    ],
]);

Resolver class example:

$schema = buildSchema($source, [
    'Query' => [
        'hero' => new HeroResolver(),
    ],
]);

Resolver middleware

If you find yourself writing the same logic in multiple resolvers you should consider using middleware. Resolver middleware allow you to efficiently manage functionality across multiple resolvers.

Before middleware example:

$resolverRegistry = new ResolverRegristry([
    'Query' => [
        'hero' => function ($rootValue, $arguments) {
            return getHero($arguments['episode'] ?? null);
        },
    ],
], [
    'middleware' => [new BeforeMiddleware()],
]);
$schema = buildSchema($source, $resolverRegistry);
class BeforeMiddleware implements ResolverMiddlewareInterface
{
    public function resolve(callable $resolveCallback, $rootValue, array $arguments, $context, ResolveInfo $info) {
        $newRootValue = $this->doSomethingBefore();
        return $resolveCallback($newRootValue, $arguments, $context, $info);
    }
}

After middleware example:

$resolverRegistry = new ResolverRegristry([
    'Query' => [
        'hero' => function ($rootValue, $arguments) {
            return getHero($arguments['episode'] ?? null);
        },
    ],
], [
    'middleware' => [new AfterMiddleware()],
]);
$schema = buildSchema($source, $resolverRegistry);
class AfterMiddleware implements ResolverMiddlewareInterface
{
    public function resolve(callable $resolveCallback, $rootValue, array $arguments, $context, ResolveInfo $info) {
        $result = $resolveCallback($rootValue, $arguments, $context, $info);
        $this->doSomethingAfter();
        return $result;
    }
}

Resolver middleware can be useful for a number of things; such as logging, input sanitization, performance measurement, authorization and caching.

If you want to learn more about schemas you can refer to the specification.

Execution

Queries

To execute a query against your schema you need to call the graphql function and pass it your schema and the query you wish to execute. You can also run mutations and subscriptions by changing your query.

$query = '
query HeroNameQuery {
  hero {
    name
  }
}';

$result = graphql($schema, $query);

If you want to learn more about queries you can refer to the specification.

Resolvers

Each type in a schema has a resolver associated with it that allows for resolving the actual value. However, most types do not need a custom resolver, because they can be resolved using the default resolver. Usually these resolvers are lambda functions, but you can also define your own resolvers by extending AbstractTypeResolver or AbstractFieldResolver. Alternatively you can also implement the ResolverInterface directly.

A resolver function receives four arguments:

  • $rootValue The parent object, which can also be null in some cases
  • $arguments The arguments provided to the field in the query
  • $context A value that is passed to every resolver that can hold important contextual information
  • $info A value which holds field-specific information relevant to the current query

Lambda function example:

function ($rootValue, array $arguments, $context, ResolveInfo $info): string {
    return [
        'type'       => 'Human',
        'id'         => '1000',
        'name'       => 'Luke Skywalker',
        'friends'    => ['1002', '1003', '2000', '2001'],
        'appearsIn'  => ['NEWHOPE', 'EMPIRE', 'JEDI'],
        'homePlanet' => 'Tatooine',
    ];
}

Type resolver example:

class HumanResolver extends AbstractTypeResolver
{
    public function resolveName($rootValue, array $arguments, $context, ResolveInfo $info): string
    {
        return $rootValue['name'];
    }
}

Field resolver example:

class NameResolver extends AbstractFieldResolver
{
    public function resolve($rootValue, array $arguments, $context, ResolveInfo $info): string
    {
       return $rootValue['name'];
    }
}

The N+1 problem

The resolver function can return a value, a promise or an array of promises. This resolver function below illustrates how to use promise to solve the N+1 problem, the full example can be found in this test case.

$movieType = newObjectType([
    'fields' => [
        'title'    => ['type' => stringType()],
        'director' => [
            'type'    => $directorType,
            'resolve' => function ($movie, $args) {
                DirectorBuffer::add($movie['directorId']);
                
                return new Promise(function (callable $resolve, callable $reject) use ($movie) {
                    DirectorBuffer::loadBuffered();
                    $resolve(DirectorBuffer::get($movie['directorId']));
                });
            }
        ]
    ]
]);

Variables

You can pass in variables when executing a query by passing them to the graphql function.

$query = '
query HeroNameQuery($id: ID!) {
  hero(id: $id) {
    name
  }
}';

$variables = ['id' => '1000'];

$result = graphql($schema, $query, null, null, $variables);

Context

In case you need to pass in some important contextual information to your queries you can use the $contextValues argument on graphql to do so. This data will be passed to all of your resolvers as the $context argument.

$contextValues = [
    'currentlyLoggedInUser' => $currentlyLoggedInUser,
];

$result = graphql($schema, $query, null, $contextValues, $variables);

Scalars

The leaf nodes in a schema are called scalars and each scalar resolves to some concrete data. The built-in, or specified scalars in GraphQL are the following:

  • Boolean
  • Float
  • Int
  • ID
  • String

Custom scalars

In addition to the specified scalars you can also define your own custom scalars and let your schema know about them by passing them to the buildSchema function as part of its $options argument.

Custom Date scalar type example:

$dateType = newScalarType([
    'name'         => 'Date',
    'serialize'    => function ($value) {
        if ($value instanceof DateTime) {
            return $value->format('Y-m-d');
        }
        return null;
    },
    'parseValue'   => function ($value) {
        if (\is_string($value)){
            return new DateTime($value);
        }
        return null;
    },
    'parseLiteral' => function ($node) {
        if ($node instanceof StringValueNode) {
            return new DateTime($node->getValue());
        }
        return null;
    },
]);

$schema = buildSchema($source, [
    'Query' => QueryResolver::class,
    [
        'types' => [$dateType],
    ],
]);

Every scalar has to be coerced, which is done by three different functions. The serialize function converts a PHP value into the corresponding output value. TheparseValue function converts a variable input value into the corresponding PHP value and the parseLiteral function converts an AST literal into the corresponding PHP value.

Advanced usage

If you are looking for something that isn't yet covered by this documentation your best bet is to take a look at the tests in this project. You'll be surprised how many examples you'll find there.

Integration

Laravel

Here is an example that demonstrates how you can use this library in your Laravel project. You need an application service to expose this library to your application, a service provider to register that service, a controller and a route for handling the GraphQL POST requests.

app/GraphQL/GraphQLService.php

class GraphQLService
{
    private $schema;

    public function __construct(Schema $schema)
    {
        $this->schema = $schema;
    }

    public function executeQuery(string $query, array $variables, ?string $operationName): array
    {
        return graphql($this->schema, $query, null, null, $variables, $operationName);
    }
}

app/GraphQL/GraphQLServiceProvider.php

class GraphQLServiceProvider
{
    public function register()
    {
        $this->app->singleton(GraphQLService::class, function () {
            $schemaDef = \file_get_contents(__DIR__ . '/schema.graphqls');

            $executableSchema = buildSchema($schemaDef, [
                'Query' => QueryResolver::class,
            ]);

            return new GraphQLService($executableSchema);
        });
    }
}

app/GraphQL/GraphQLController.php

class GraphQLController extends Controller
{
    private $graphqlService;

    public function __construct(GraphQLService $graphqlService)
    {
        $this->graphqlService = $graphqlService;
    }

    public function handle(Request $request): JsonResponse
    {
        $query         = $request->get('query');
        $variables     = $request->get('variables') ?? [];
        $operationName = $request->get('operationName');

        $result = $this->graphqlService->executeQuery($query, $variables, $operationName);

        return response()->json($result);
    }
}

routes/api.php

Route::post('/graphql', 'app\GraphQL\GraphQLController@handle');

Contributors

This project exists thanks to all the people who contribute. Contribute.

Backers

Thank you to all our backers! 🙏 Become a backer

Sponsors

Support this project by becoming a sponsor. Your logo will show up here with a link to your website. Become a sponsor

License

See LICENCE.

More Repositories

1

react-foundation

Foundation as React components.
JavaScript
601
star
2

lumen-cors

CORS module for the Lumen PHP framework.
PHP
96
star
3

react-boilerplate

This project is deprecated. Please use CRA instead.
JavaScript
86
star
4

react-starter

Our starter template for immutable React applications with Redux.
JavaScript
71
star
5

lumen-elasticsearch

Simple wrapper of https://github.com/elastic/elasticsearch-php for the Lumen PHP framework.
PHP
57
star
6

react-flow-types

Flow type definitions for React
JavaScript
51
star
7

redux-fetch-data

Redux utility library for fetching data using promises on both server and client.
JavaScript
40
star
8

lumen-file-manager

File manager module for the Lumen PHP framework.
PHP
38
star
9

lumen-doctrine

Doctrine module for the Lumen PHP framework.
PHP
36
star
10

lumen-newrelic

New Relic instrumentation for the Lumen framework
PHP
28
star
11

lumen-oauth2

OAuth2 module for the Lumen PHP framework.
PHP
27
star
12

lumen-search

Search module for the Lumen PHP framework.
PHP
26
star
13

lumen-fractal

Fractal module for the Lumen PHP framework.
PHP
26
star
14

lumen-graphql

GraphQL module for the Laravel and Lumen PHP frameworks.
PHP
25
star
15

yii2-account

A module for the Yii framework that provides common account functionality.
PHP
24
star
16

lumen-rbac

RBAC module for the Lumen PHP framework based on Overseer.
PHP
22
star
17

lumen-image-manager

Image manager module for the Lumen PHP framework.
PHP
21
star
18

lumen-core

Core module for the Lumen PHP framework.
PHP
20
star
19

yii-account

Extension that provides basic account functionality for the Yii PHP framework.
PHP
19
star
20

lumen-doctrine-mongodb-odm

Doctrine MongoDB ODM bindings for lumen
PHP
15
star
21

paytrail-php

Paytrail REST client for PHP.
PHP
13
star
22

docs

12
star
23

yii-emailer

Extension for creating and sending emails for the Yii PHP framework.
PHP
12
star
24

yii2-file-manager

A low-level interface for managing files in Yii applications
PHP
12
star
25

docker-nginx-php-node

Docker container for a web server running Nginx, PHP-FPM and Node.js
ApacheConf
11
star
26

lumen-sparkpost

SparkPost module for the Lumen PHP framework.
PHP
11
star
27

lumen-dynamodb

Module for AWS DynamoDB
PHP
10
star
28

yii-paymentmanager

Payment manager for the Yii PHP framework.
PHP
9
star
29

yii-audit

Extension that collect audit data for the Yii PHP framework.
PHP
7
star
30

lumen-chained-exception-handler

A chained exception handler for the Lumen framework
PHP
7
star
31

yii-parsley

Parsley.js integration for the Yii PHP framework.
PHP
6
star
32

react-foundation-docs

Documentation for React + Foundation
JavaScript
6
star
33

lumen-cloudinary

Cloudinary module for Lumen PHP framework.
PHP
6
star
34

yii-paytrail

Paytrail implementation for the yii-payment extension.
PHP
6
star
35

lumen-serializer

Serializer module for the Lumen PHP framework.
PHP
6
star
36

lumen-oauth2-dynamodb

DynamoDB support for the lumen-oauth2 module
PHP
5
star
37

graphql-relay-php

WIP: Relay support for https://github.com/digiaonline/graphql-php
PHP
5
star
38

outdoors-sports-map

Helsingin kaupungin Ulkoliikuntakartta.
HTML
5
star
39

lumen-mandrill-mailer

Mandrill mailer for the Lumen PHP framework
PHP
4
star
40

nettineuvoja

Nettineuvoja
JavaScript
4
star
41

lumen-file-manager-dynamodb

DynamoDB support for the lumen-file-manager module
PHP
4
star
42

lumen-contentful-sync

A framework for synchronizing content from Contentful to a Lumen API
PHP
3
star
43

vagrant-ansible-lemp

Local Vagrant LEMP-environment
PHP
3
star
44

yii-cms

DEPRECATED CMS module for the Yii PHP framework.
3
star
45

drupal-scripts

Some handy tools for working with Drupal
Shell
3
star
46

oops

The open-sourced version of our ops
Shell
3
star
47

yii-sphinx

DEPRECATED Sphinx search extension for Yii framework
2
star
48

techradar

Our company Tech Radar
HTML
2
star
49

graphql-datetime-scalar-php

Date, Time and DateTime scalar types for our GraphQL implementation
PHP
2
star
50

cuddly-potato

Hammertime!
Go
2
star
51

eslint-config-nordsoftware

JavaScript
2
star
52

api-recorder

Recorder and player for HTTP requests in Kotlin
Kotlin
2
star
53

wifi-dashboard

Self-contained dashboard interface for the (old) office network
JavaScript
2
star
54

thor

Thor is our company robot.
CoffeeScript
2
star
55

yii-rest

Simple REST API for the Yii application framework.
PHP
2
star
56

yii-mandrill

DEPRECATED Mandrill integration for Yii
2
star
57

yii-utils

Utility classes for the Yii PHP framework.
PHP
2
star
58

vagrant-drupal

Vagrant environment for Drupal projects
2
star
59

lumen-sns-middleware

Collection of middleware and helpers for dealing with AWS SNS notifications
PHP
2
star
60

generator-nord-backbone

Yeoman generator that scaffolds our Backbone applications.
JavaScript
1
star
61

agile-workshops

1
star
62

yii-parsley2

DEPRECATED Parsley.js v2 integration for the Yii PHP framework.
PHP
1
star
63

smart-form

JavaScript
1
star
64

yii2-image-manager

A low-level interface for managing images in Yii applications
PHP
1
star
65

nordsoftware.github.io

The old homepage for open source at Nord Software. New site:
JavaScript
1
star
66

vuejs-01-intro

HTML
1
star
67

lumen-contentful

A Lumen service provider for Contentful
PHP
1
star
68

yii-ga-reports-api

DEPRECATED Google Analytics Reports API extension for Yii framework
1
star
69

image-manipulation-service

Simple image manipulation service
PHP
1
star
70

docker-node-grunt-bower

Docker container running Node.js, Grunt and Bower
1
star
71

ionic-app

Project template for any Ionic application
C++
1
star
72

docker-nginx-php

Docker container for a web server running Nginx and PHP-FPM based on Debian
ApacheConf
1
star