{"id":13499282,"url":"https://github.com/youshido-php/GraphQL","last_synced_at":"2025-03-29T04:30:58.446Z","repository":{"id":2603310,"uuid":"46961954","full_name":"youshido-php/GraphQL","owner":"youshido-php","description":"Pure PHP realization of GraphQL protocol","archived":false,"fork":false,"pushed_at":"2024-05-30T08:53:08.000Z","size":2914,"stargazers_count":710,"open_issues_count":18,"forks_count":106,"subscribers_count":35,"default_branch":"master","last_synced_at":"2024-10-29T15:19:10.300Z","etag":null,"topics":["graphql","graphql-php","graphql-schema"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/youshido-php.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-11-27T05:44:33.000Z","updated_at":"2024-08-24T00:07:16.000Z","dependencies_parsed_at":"2024-06-18T11:14:15.729Z","dependency_job_id":"4609c7bc-bec2-4bbb-b69c-e51a5adc19f9","html_url":"https://github.com/youshido-php/GraphQL","commit_stats":{"total_commits":652,"total_committers":49,"mean_commits":"13.306122448979592","dds":0.5429447852760736,"last_synced_commit":"b080756d8d441d68473b12569bfe621f5be73f7b"},"previous_names":[],"tags_count":93,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/youshido-php%2FGraphQL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/youshido-php%2FGraphQL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/youshido-php%2FGraphQL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/youshido-php%2FGraphQL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/youshido-php","download_url":"https://codeload.github.com/youshido-php/GraphQL/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245773156,"owners_count":20669719,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graphql","graphql-php","graphql-schema"],"created_at":"2024-07-31T22:00:31.818Z","updated_at":"2025-03-29T04:30:57.926Z","avatar_url":"https://github.com/youshido-php.png","language":"PHP","readme":"# Looking for Maintainers!\n\nUnfortunatelly, we cannot longer support this package and are looking for someone to take the ownership.\nCurrently Only PRs with bugfixes and not breaking BC are being merged.\nIt's very sad to acknowledge this, but we hope that someone can take it further with the community.\n\nPlease, PM @viniychuk if you are interested in taking over.\n\n# GraphQL\n\n[![Join the chat at https://gitter.im/Youshido/GraphQL](https://badges.gitter.im/Youshido/GraphQL.svg)](https://gitter.im/Youshido/GraphQL?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![Latest Stable Version](https://poser.pugx.org/youshido/graphql/v/stable)](https://packagist.org/packages/youshido/graphql)\n[![Build Status](https://travis-ci.org/youshido-php/GraphQL.svg?branch=master)](https://travis-ci.org/youshido-php/GraphQL)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/Youshido/GraphQL/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/Youshido/GraphQL/?branch=master)\n[![Code Coverage](https://scrutinizer-ci.com/g/Youshido/GraphQL/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/Youshido/GraphQL/?branch=master)\n[![SensioLabsInsight](https://insight.sensiolabs.com/projects/8b8ab2a2-32fb-4298-a986-b75ca523c7c9/mini.png)](https://insight.sensiolabs.com/projects/8b8ab2a2-32fb-4298-a986-b75ca523c7c9)\n\nThis is a pure PHP realization of the GraphQL protocol based on the working draft of the official GraphQL Specification located on http://facebook.github.io/graphql/.\n\nGraphQL is a query language for APIs. It brings a new paradigm to the world of client-server communication and delivers a much more predictable behavior and smallest possible over-the-wire responses to any request. \nGraphQL advanced in many ways and has fundamental quality improvements:\n\n - strongly typed communication protocol makes both client and server predictable and more stable\n - encourages you to build a constantly evolving APIs and not use versions in the endpoints\n - bulk requests and responses to avoiding waiting for multiple HTTP handshakes\n - easily generated documentation and incredibly intuitive way to explore created API\n - clients will be much less likely to require backend changes\n\n\u003e Current package is and will be trying to be kept up to date with the latest revision of the official GraphQL Specification which is now of April 2016.\n\n\u003e Symfony bundle is available by the link – [http://github.com/Youshido/GraphqlBundle](http://github.com/Youshido/GraphqlBundle)\n\n\u003e If you have any questions or suggestions – let's talk on [GraphQL Gitter channel](https://gitter.im/Youshido/GraphQL)\n\n## Table of Contents\n\n* [Getting Started](#getting-started)\n* [Installation](#installation)\n* [Example – Creating Blog Schema](#tutorial--creating-blog-schema)\n  * [Inline approach](#inline-approach)\n  * [Object Oriented approach](#object-oriented-approach)\n  * [Choosing approach for your project](#choosing-approach-for-your-project)\n* [Query Documents](#query-documents)\n* [Type System](#type-system)\n  * [Scalar Types](#scalar-types)\n  * [Objects](#objects)\n  * [Interfaces](#interfaces)\n  * [Enums](#enums)\n  * [Unions](#unions)\n  * [Lists](#lists)\n  * [Input Objects](#input-objects)\n  * [Non-Null](#non-null)\n* [Building your schema](#building-your-schema)\n  * [Abstract type classes](#abstract-type-classes)\n  * [Mutation helper class](#mutation-helper-class)\n* [Useful information](#useful-information)\n  * [GraphiQL tool](#graphiql-tool)\n\n## Getting Started\n\nYou should be better off starting with some examples and \"Star Wars\" become a somewhat \"Hello world\" for the GraphQL implementations.\nIf you're looking just for that – you can get it via this link – [Star Wars example](https://github.com/Youshido/GraphQL/tree/master/Tests/StarWars).\nOn the other hand, we prepared a step-by-step guide for those who wants to get up to speed bit by bit.\n\n### Installation\n\nInstall GraphQL package using composer. If you're not familiar with it, you should check out their [manual](https://getcomposer.org/doc/00-intro.md).\nRun `composer require youshido/graphql`.\n\nAlternatively you can run the following commands:\n```sh\nmkdir graphql-test \u0026\u0026 cd graphql-test\ncomposer init -n\ncomposer require youshido/graphql\n```\n\nNow you're ready to create your `GraphQL Schema` and check if everything works fine.\nYour first GraphQL app will be able to receive `currentTime` request and response with a formatted time string.\n\u003e you can find this example in the examples directory – [01_sandbox](https://github.com/Youshido/GraphQL/tree/master/examples/01_sandbox).\n\nCreate an `index.php` file with the following content:\n```php\n\u003c?php\nnamespace Sandbox;\n\nuse Youshido\\GraphQL\\Execution\\Processor;\nuse Youshido\\GraphQL\\Schema\\Schema;\nuse Youshido\\GraphQL\\Type\\Object\\ObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nrequire_once 'vendor/autoload.php';\n\n$processor = new Processor(new Schema([\n    'query' =\u003e new ObjectType([\n        'name' =\u003e 'RootQueryType',\n        'fields' =\u003e [\n            'currentTime' =\u003e [\n                'type' =\u003e new StringType(),\n                'resolve' =\u003e function() {\n                    return date('Y-m-d H:ia');\n                }\n            ]\n        ]\n    ])\n]));\n\n$processor-\u003eprocessPayload('{ currentTime }');\necho json_encode($processor-\u003egetResponseData()) . \"\\n\";\n```\n\nYou can now execute `php index.php` and get a response with your current time:\n ```js\n {\n    data: { currentTime: \"2016-05-01 19:27pm\" }\n }\n ```\nJust like that, you have created a `GraphQL Schema` with a `field` `currentTime` of type `String` and `resolver` for it. Don't worry if you don't know what the `field`, `type` and `resolver` mean here, you'll learn along the way.\n\nIf you're having any troubles – here're some troubleshooting points:\n* check that you have the latest composer version (`composer self-update`)\n* make sure your `index.php` file has been created in the same directory that you have `vendor` folder in (presumably it's `graphql-test` folder)\n* last but not least, check that you have php-cli installed and running and it's version \u003e= 5.5 (`php -v`)\n\nAlso, you can always check if script from the [examples folder](https://github.com/Youshido/GraphQL/tree/master/examples/01_sandbox) work.\n\n## Tutorial – Creating Blog Schema\n\nFor our learning example we'll architect a GraphQL Schema for a Blog.\nYou'll probably be using our package along with your favorite framework (we have a Symfony version [here](http://github.com/Youshido/GraphqlBundle)), but for the purpose of this tutorial we're keeping it all examples as plain php code.\n\u003e\u003e (Complete example of the Blog schema available by the following link https://github.com/Youshido/GraphQL/tree/master/examples/02_blog)\n\nOur Blog will have `Users` who can write `Posts` and leave `Comments`. Also, there will be a `LikePost` operation that could be performed by anyone.\nLet's start with `Post`. Take a look at the query that returns `title` and `summary` of the latest Post:\n\u003e\u003e GraphQL query is a simple text query structured very much similar to the json format.\n ```\n latestPost {\n     title,\n     summary\n }\n ```\n\nSupposedly server should reply with a relevant json response:\n ```js\n {\n    data: {\n        latestPost: {\n            title: \"This is a post title\",\n            summary: \"This is a post summary\"\n        }\n    }\n }\n ```\nIt looks very simple and straight forward, so let's go ahead and write code that can handle this request.\n\n### Creating Post schema\n\nWe'll take a quick look on different approaches you can use to define your schema.\nEach of them has it's own pros and cons, inline approach might seem to be easier and faster when object oriented gives you more flexibility and freedom as your project grows.\nYou should definitely use OOP approach every time you can reuse the type you're creating.\n\nWe're going to create `RootQueryType` with one field `latestPost`.\nEvery `GraphQL Field` has a `type`(e.g. String, Int, Boolean) and it could be of a different `kind`(e.g. Scalar, Enum, List). You can read more about it in the [official documentation](https://facebook.github.io/graphql/#sec-Type-System), but for now you can think of `field of a type` like about `instance of a class`.\n\n#### Inline approach\n\nYou can create `inline-index.php` file in your project folder and paste the following code there\n\n**inline-index.php**\n```php\n\u003c?php\nnamespace InlineSchema;\n\nuse Youshido\\GraphQL\\Execution\\Processor;\nuse Youshido\\GraphQL\\Schema\\Schema;\nuse Youshido\\GraphQL\\Type\\Object\\ObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\n// including autoloader\nrequire_once __DIR__ . '/vendor/autoload.php';\n\n// instantiating Processor and setting the schema\n$processor = new Processor(new Schema([\n    'query' =\u003e new ObjectType([\n        // root query by convention has a name RootQueryType\n        'name'   =\u003e 'RootQueryType',\n        'fields' =\u003e [\n            'latestPost' =\u003e [\n                'type'    =\u003e new ObjectType([ // Post type is being created as ObjectType\n                    'name'    =\u003e 'Post', // name of our type – \"Post\"\n                    'fields'  =\u003e [\n                        'title'   =\u003e new StringType(),  // defining \"title\" field, type - String\n                        'summary' =\u003e new StringType(),  // defining \"summary\" field, type - String\n                    ],\n                ]),\n                'resolve' =\u003e function () {          // resolver for latestPost field\n                    return [                        // for now it returns a static array with data\n                        \"title\"   =\u003e \"New approach in API has been revealed\",\n                        \"summary\" =\u003e \"In two words - GraphQL Rocks!\",\n                    ];\n                }\n            ]\n        ]\n    ])\n]));\n\n// creating payload and running it through processor\n$payload = '{ latestPost { title, summary } }';\n$processor-\u003eprocessPayload($payload);\n// displaying result\necho json_encode($processor-\u003egetResponseData()) . \"\\n\";\n```\n\nTo check if everything is working – execute inline-index.php: `php inline-index.php`\nYou should see response as the json encoded object `latestPost` inside the `data` section:\n ```js\n {\n    data: {\n        latestPost: {\n            title: \"New approach in API has been revealed\",\n            summary: \"In two words - GraphQL Rocks!\"\n        }\n    }\n }\n ```\n\n\u003e Try to play with the code by removing one field from the request or by changing the resolve function.\n\n#### Object oriented approach\n\nIt's a common situation when you need to use the same custom type in different places, so we're going to create a separate class for the `PostType` and use it in our `GraphQL Schema`.\nTo keep everything structured we're going to put this and all our future classes into the `Schema` folder.\n\nCreate a file `Schema/PostType.php` and put the following code in there:\n```php\n\u003c?php\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass PostType extends AbstractObjectType   // extending abstract Object type\n{\n\n    public function build($config)  // implementing an abstract function where you build your type\n    {\n        $config\n            -\u003eaddField('title', new StringType())       // defining \"title\" field of type String\n            -\u003eaddField('summary', new StringType());    // defining \"summary\" field of type String\n    }\n\n    public function getName()\n    {\n        return \"Post\";  // if you don't do getName – className without \"Type\" will be used\n    }\n\n}\n```\n\nNow let's create the main entry point for this example – `index.php`:\n```php\n\u003c?php\n\nnamespace Examples\\Blog;\n\nuse Examples\\Blog\\Schema\\PostType;\nuse Youshido\\GraphQL\\Execution\\Processor;\nuse Youshido\\GraphQL\\Schema\\Schema;\nuse Youshido\\GraphQL\\Type\\Object\\ObjectType;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\nrequire_once __DIR__ . '/Schema/PostType.php';       // including PostType definition\n\n$rootQueryType = new ObjectType([\n    'name' =\u003e 'RootQueryType',\n    'fields' =\u003e [\n        'latestPost' =\u003e [\n            'type'    =\u003e new PostType(),\n            'resolve' =\u003e function ($source, $args, $info)\n            {\n                return [\n                    \"title\"   =\u003e \"New approach in API has been revealed\",\n                    \"summary\" =\u003e \"In two words - GraphQL Rocks!\",\n                ];\n            }\n        ]\n    ]\n]);\n\n$processor = new Processor(new Schema([\n    'query' =\u003e $rootQueryType\n]));\n$payload = '{ latestPost { title, summary } }';\n\n$processor-\u003eprocessPayload($payload);\necho json_encode($processor-\u003egetResponseData()) . \"\\n\";\n```\n\nEnsure everything is working properly by running `php index.php`. You should see the same response you saw for the inline approach.\n\nNext step would be to create a separate class for the latestPostField by extending `AbstractField` class:\n**Schema/LatestPostField.php**\n```php\n\u003c?php\n\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Execution\\ResolveInfo;\nuse Youshido\\GraphQL\\Field\\AbstractField;\n\nclass LatestPostField extends AbstractField\n{\n    public function getType()\n    {\n        return new PostType();\n    }\n\n    public function resolve($value, array $args, ResolveInfo $info)\n    {\n        return [\n            \"title\"   =\u003e \"New approach in API has been revealed\",\n            \"summary\" =\u003e \"In two words - GraphQL Rocks!\",\n        ];\n    }\n}\n```\nAnd now we can update our `index.php`:\n```php\n\u003c?php\n\nnamespace Examples\\Blog;\n\nuse Examples\\Blog\\Schema\\LatestPostField;\nuse Youshido\\GraphQL\\Execution\\Processor;\nuse Youshido\\GraphQL\\Schema\\Schema;\nuse Youshido\\GraphQL\\Type\\Object\\ObjectType;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\nrequire_once __DIR__ . '/Schema/PostType.php';       // including PostType definition\nrequire_once __DIR__ . '/Schema/LatestPostField.php';\n\n$rootQueryType = new ObjectType([\n    'name' =\u003e 'RootQueryType',\n    'fields' =\u003e [\n        new LatestPostField()\n    ]\n]);\n\n$processor = new Processor(new Schema([\n    'query' =\u003e $rootQueryType\n]));\n$payload = '{ latestPost { title, summary } }';\n\n$processor-\u003eprocessPayload($payload);\necho json_encode($processor-\u003egetResponseData()) . \"\\n\";\n```\n\n### Choosing approach for your project\n\nWe would recommend to stick to object oriented approach for the several reasons (that matter the most for the GraphQL specifically):\n - makes your `Types` reusable\n - adds an ability to refactor your schema using IDEs\n - autocomplete to help you avoid typos\n - much easier to navigate through your Schema when project grows\n\n With that being said, we use inline approach a lot to explore and bootstrap ideas or to develop simple fields/resolver that are going to be used in one place only.\n With the inline approach you can be fast and agile in creating mock-data server to test your frontend or mobile client.\n\n\u003e **Use valid Names**  \n\u003e We highly recommend to get familiar with the [official GraphQL Specification](https://facebook.github.io/graphql/#sec-Language.Query-Document)\n\u003e Remember that valid identifier in GraphQL should follow the pattern `/[_A-Za-z][_0-9A-Za-z]*/`.\n\u003e That means any identifier should consist of a latin letter, underscore, or a digit and cannot start with a digit.\n\u003e **Names are case sensitive**\n\nWe'll continue to work on the Blog Schema to explore all essentials details of developing GraphQL server.\n\n## Query Documents\n\nIn GraphQL terms – query document describe a complete request received by GraphQL service.\nIt contains list of *Operations* and *Fragments*. Both are fully supported by our PHP library.\nThere are two types of *Operations* in GraphQL:\n- *Query* – a read only request that is not supposed to do any changes on the server\n- *Mutation* – a request that changes(mutate) data on the server followed by a data fetch\n\nYou've already seen examples of `Query` with `latestPost` and `currentTime`, so let's define a simple Mutation that will provide API to *Like* the Post.\nHere's sample request and response of `likePost` mutation:\n\n*request*\n```\nmutation {\n  likePost(id: 5)\n}\n```\n*response*\n```js\n{\n  data: { likePost: 2 }\n}\n```\n\u003e Any Operation has a response type and in this case the likePost mutation type is `Int`\n\nNote, that the response type of this mutation is a scalar `Int`.\nOf course in real life you'll more likely have a response of type `Post` for such mutation, but we're going to implement code for a simple example above and even keep it inside `index.php`:\n\n```php\n\u003c?php\n\nnamespace Examples\\Blog;\n\nuse Examples\\Blog\\Schema\\LatestPostField;\nuse Youshido\\GraphQL\\Execution\\Processor;\nuse Youshido\\GraphQL\\Schema\\Schema;\nuse Youshido\\GraphQL\\Type\\NonNullType;\nuse Youshido\\GraphQL\\Type\\Object\\ObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\IntType;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\nrequire_once __DIR__ . '/Schema/PostType.php';       // including PostType definition\nrequire_once __DIR__ . '/Schema/LatestPostField.php';\n\n$rootQueryType = new ObjectType([\n    'name'   =\u003e 'RootQueryType',\n    'fields' =\u003e [\n        new LatestPostField()\n    ]\n]);\n\n$rootMutationType = new ObjectType([\n    'name'   =\u003e 'RootMutationType',\n    'fields' =\u003e [\n        // defining likePost mutation field\n        'likePost' =\u003e [\n            // we specify the output type – simple Int, since it doesn't have a structure\n            'type'    =\u003e new IntType(),\n            // we need a post ID and we set it to be required Int\n            'args'    =\u003e [\n                'id' =\u003e new NonNullType(new IntType())\n            ],\n            // simple resolve function that always returns 2\n            'resolve' =\u003e function () {\n                return 2;\n            },\n        ]\n    ]\n]);\n\n$processor = new Processor(new Schema([\n    'query'    =\u003e $rootQueryType,\n    'mutation' =\u003e $rootMutationType\n]));\n$payload   = 'mutation { likePost(id: 5) }';\n\n$processor-\u003eprocessPayload($payload);\necho json_encode($processor-\u003egetResponseData()) . \"\\n\";\n```\n\nRun `php index.php`, you should see a valid response:\n```js\n{\"data\":{\"likePost\":2}}\n```\n\nNow, let's make our `likePost` mutation to return the whole `Post` as a result.\nFirst, we'll add `likesCount` field to the `PostType`:\n```php\n\u003c?php\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\IntType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass PostType extends AbstractObjectType\n{\n\n    public function build($config)\n    {\n        // you can define fields in a single addFields call instead of chaining multiple addField()\n        $config-\u003eaddFields([\n            'title'      =\u003e new StringType(),\n            'summary'    =\u003e new StringType(),\n            'likesCount' =\u003e new IntType()\n        ]);\n    }\n\n    // Since our class named by a convention, we can remove getName() method\n}\n```\nSecondly, modify `resolve` function in `LatestPostField`:\n```php\npublic function resolve($value, array $args, ResolveInfo $info)\n{\n    return [\n        \"title\"      =\u003e \"New approach in API has been revealed\",\n        \"summary\"    =\u003e \"In two words - GraphQL Rocks!\",\n        \"likesCount\" =\u003e 2\n    ];\n}\n```\n\nLastly, we're going to change `Mutation Type` from `IntType` to `PostType` and update the `resolve` function to be compliant with the the new type and update the request:\n```php\n\u003c?php\n// ...\n$rootMutationType = new ObjectType([\n    'name'   =\u003e 'RootMutationType',\n    'fields' =\u003e [\n        'likePost' =\u003e [\n            'type'    =\u003e new PostType(),\n            'args'    =\u003e [\n                'id' =\u003e new NonNullType(new IntType())\n            ],\n            'resolve' =\u003e function () {\n                return [\n                    'title'     =\u003e 'New approach in API has been revealed',\n                    'summary'   =\u003e 'In two words - GraphQL Rocks!',\n                    'likesCount' =\u003e 2\n                ];\n            },\n        ]\n    ]\n]);\n// ...\n$payload   = 'mutation { likePost(id: 5) { title, likesCount } }';\n//...\n```\n\nExecute `php index.php`, you should see `title` and `likesCount` in response. We can now try to use `id: 5` that we're passing as a parameter to our mutation:\n```php\n$rootMutationType = new ObjectType([\n    'name'   =\u003e 'RootMutationType',\n    'fields' =\u003e [\n        'likePost' =\u003e [\n            'type'    =\u003e new PostType(),\n            'args'    =\u003e [\n                'id' =\u003e new NonNullType(new IntType())\n            ],\n            'resolve' =\u003e function ($source, $args, $resolveInfo) {\n                return [\n                    'title'      =\u003e 'Title for the post #' . $args['id'], // we can be sure that $args['id'] is always set\n                    'summary'    =\u003e 'In two words - GraphQL Rocks!',\n                    'likesCount' =\u003e 2\n                ];\n            },\n        ]\n    ]\n]);\n```\n\nNow you have a basic understanding of how queries and mutations are structured and ready to move on to the details of the GraphQL Type System and PHP-specific features of the GraphQL server architecture.\n\n## Type System\n\n*Type* is an atom of definition in GraphQL Schema. Every field, object, or argument has a type. GraphQL is a strongly typed language.\nThere are `system types` and `custom types` defined specifically for the application, in our app we'll have custom  types  `Post`, `User`, `Comment`, etc. Your custom types are usually built on top of GraphQL system types.\n\n### Scalar Types\n\nList of GraphQL Scalar types:\n- Int\n- Float\n- String\n- Boolean\n- Id (serialized as String per [spec](https://facebook.github.io/graphql/#sec-ID))\n\nIn addition, we implemented some types that might be useful and which we're considering to be scalar as well:\n- Timestamp\n- DateTimeTz (» RFC 2822 formatted date with TimeZone)\n\u003e\u003e Date and DateTime are deprecated and will be remove. We're going to provide an easy solution how to replace them in your project\n\nIf you will ever need to define a new Scalar type, you can do that by extending from the `AbstractScalarType` class.\n\u003e usage of scalar types will be shown in combination with other types down here\n\n### Objects\n\nEvery entity in your business logic will probably have a class that represents it's type. That class must be either extended from the `AbstractObjectType` or created as an instance of `ObjectType`.\nIn our blog example we used `ObjectType` to create an inline `PostType` and extended `AbstractObjectType` to create a `PostType` class in the object oriented approach.\n\nLet's take a closer look at the structure of `PostType` and see what parameters we can configure for each field.\n\n```php\n\u003c?php\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\BooleanType;\nuse Youshido\\GraphQL\\Type\\Scalar\\IntType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass PostType extends AbstractObjectType\n{\n\n    public function build($config)\n    {\n        // you can define fields in a single addFields call instead of chaining multiple addField()\n        $config-\u003eaddFields([\n            'title'      =\u003e [\n                'type' =\u003e new StringType(),\n                'description'       =\u003e 'This field contains a post title',\n                'isDeprecated'      =\u003e true,\n                'deprecationReason' =\u003e 'field title is now deprecated',\n                'args'              =\u003e [\n                    'truncate' =\u003e new BooleanType()\n                ],\n                'resolve'           =\u003e function ($source, $args) {\n                    return (!empty($args['truncate'])) ? explode(' ', $source['title'])[0] . '...' : $source['title'];\n                }\n            ],\n            'summary'    =\u003e new StringType(),\n            'likesCount' =\u003e new IntType()\n        ]);\n    }\n}\n```\nNow you can change `index.php` to perform requests like these:\n```php\n$payload   = 'mutation { likePost(id: 5) { title(truncate: true), likesCount } }';\n```\nAs you can see we now have argument `id` for the mutation and another argument `truncate` for the field `title` inside `PostTitle`. We can use it everywhere that `PostType` is being used.\n\n### Interfaces\n\nGraphQL supports `Interfaces`. You can define Interface and use it as a `Type` of an item in the `List`, or use Interface to make sure that specific objects certainly have fields you need.\nEach `InterfaceType` has to have at least one defined field and `resolveType` function. That function will be used to determine what exact `Type` will be returned by GraphQL resolver.\nLet's create a `ContentBlockInterface` that can represent a piece of content for the web page that have a `title` and a `summary` (just like our post earlier).\n```php\n\u003c?php\n/**\n * ContentBlockInterface.php\n */\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\InterfaceType\\AbstractInterfaceType;\nuse Youshido\\GraphQL\\Type\\NonNullType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass ContentBlockInterface extends AbstractInterfaceType\n{\n    public function build($config)\n    {\n        $config-\u003eaddField('title', new NonNullType(new StringType()));\n        $config-\u003eaddField('summary', new StringType());\n    }\n\n    public function resolveType($object) {\n        // since there's only one type right now this interface will always resolve PostType\n        return new PostType();\n    }\n}\n```\n\nMost often you'll be using only the `build` method to define fields and that need to be implemented.\nIn order to associate this Interface to the `PostType` we have to override it's `getInterfaces` method:\n```php\n\u003c?php\n/**\n* PostType.php\n*/\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\IntType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass PostType extends AbstractObjectType\n{\n\n    public function build($config)\n    {\n        $config-\u003eaddFields([\n            'title'      =\u003e new StringType(),\n            'summary'    =\u003e new StringType(),\n            'likesCount' =\u003e new IntType()\n        ]);\n    }\n\n    public function getInterfaces()\n    {\n        return [new ContentBlockInterface()];\n    }\n}\n\n```\nAs you might have noticed there's no `getName` method in both Interface and Type classes – that's a simplified approach available when you want to have your name exactly the same as the class name without the `Type` at the end.\n\nIf you run the script as it is right now – `php index.php`, you should get an error:\n```js\n{\"errors\":[{\"message\":\"Implementation of ContentBlockInterface is invalid for the field title\"}]}\n```\nYou've got this error because the `title` field definition in the `PostType` is different from the one described in the `ContentBlockInterface`.\nTo fix it we have to declare fields that exist in the `Interface` with the same names and types.\nWe already have `title` but it's a nullable field so we have to change it by adding a non-null wrapper – `new NonNullType(new StringType())`.\nYou can check the result by executing index.php script again, you should get the usual response.\n\nFor the convenience we also created `$config-\u003eapplyInterface()` method that could be inside `build()`:\n```php\n\u003c?php\n/**\n * PostType.php\n */\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\IntType;\n\nclass PostType extends AbstractObjectType\n{\n\n    public function build($config)\n    {\n        $config-\u003eapplyInterface(new ContentBlockInterface());\n        $config-\u003eaddFields([\n            'likesCount' =\u003e new IntType()\n        ]);\n    }\n\n    public function getInterfaces()\n    {\n        return [new ContentBlockInterface()];\n    }\n}\n```\n\n### Enums\n\nGraphQL Enums are the variation on the Scalar type, which represents one of the predefined values.\nEnums serialize as a string: the name of the represented value but can be associated with a numeric (as an example) value.\n\nTo show you how Enums work we're going to create a new class - `PostStatus`:\n```php\n\u003c?php\n/**\n * PostStatus.php\n */\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Enum\\AbstractEnumType;\n\nclass PostStatus extends AbstractEnumType\n{\n    public function getValues()\n    {\n        return [\n            [\n                'value' =\u003e 0,\n                'name'  =\u003e 'DRAFT',\n            ],\n            [\n                'value' =\u003e 1,\n                'name'  =\u003e 'PUBLISHED',\n            ]\n        ];\n    }\n}\n```\nNow, add a status field to the `PostType`:\n```php\n\u003c?php\n/**\n * PostType.php\n */\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\NonNullType;\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\IntType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass PostType extends AbstractObjectType\n{\n\n    public function build($config)\n    {\n        $config-\u003eaddFields([\n            'title'      =\u003e new NonNullType(new StringType()),\n            'summary'    =\u003e new StringType(),\n            'likesCount' =\u003e new IntType(),\n            'status'     =\u003e new PostStatus()\n        ]);\n    }\n\n    public function getInterfaces()\n    {\n        return [new ContentBlockInterface()];\n    }\n}\n```\nand update the resolve function inside latestPost field:\n```php\n\u003c?php\n\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Execution\\ResolveInfo;\nuse Youshido\\GraphQL\\Field\\AbstractField;\n\nclass LatestPostField extends AbstractField\n{\n    public function getType()\n    {\n        return new PostType();\n    }\n\n    public function resolve($value, array $args, ResolveInfo $info)\n    {\n        return [\n            \"title\"      =\u003e \"New approach in API has been revealed\",\n            \"summary\"    =\u003e \"In two words - GraphQL Rocks!\",\n            \"status\"     =\u003e 1,\n            \"likesCount\" =\u003e 2\n        ];\n    }\n}\n```\n\nRequest the `status` field in your query:\n```php\n$payload  = '{ latestPost { title, status, likesCount } }';\n```\nYou should get a result similar to the following:\n```js\n{\"data\":{\"latestPost\":{\"title\":\"New approach in API has been revealed\",\"status\":\"PUBLISHED\"}}}\n```\n\n### Unions\n\nGraphQL Unions represent an object type that could be resolved as one of a specified GraphQL Object types.\nTo get you an idea of what this is we're going to create a new query field that will return a list of unions (and get to the `ListType` after it).\n\u003e You can consider Union as a combined type that is needed mostly when you want to have a list of different objects\n\nImaging that you have a page and you need to get all content blocks for this page. Let content block be either `Post` or `Banner`.\nCreate a `BannerType`:\n```php\n\u003c?php\n/**\n * BannerType.php\n */\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass BannerType extends AbstractObjectType\n{\n    public function build($config)\n    {\n        $config\n            -\u003eaddField('title', new StringType())\n            -\u003eaddField('imageLink', new StringType());\n    }\n}\n```\nNow let's combine the `Banner` type and the `Post` type to create a `ContentBlockUnion` that will extend an `AbstractUnionType`.\nEach `UnionType` needs to define a list of types it unites by implementing the `getTypes` method and the `resolveType` method to resolve object that will be returned for each instance of the `Union`.\n```php\n\u003c?php\n/**\n * ContentBlockUnion.php\n */\n\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Union\\AbstractUnionType;\n\nclass ContentBlockUnion extends AbstractUnionType\n{\n    public function getTypes()\n    {\n        return [new PostType(), new BannerType()];\n    }\n\n    public function resolveType($object)\n    {\n        // we simple look if there's a \"post\" inside the object id that it's a PostType otherwise it's a BannerType\n        return empty($object['id']) ? null : (strpos($object['id'], 'post') !== false ? new PostType() : new BannerType());\n    }\n}\n```\n\nWe're also going to create a simple `DataProvider` that will give us test data to operate with:\n```php\n\u003c?php\n/**\n * DataProvider.php\n */\nnamespace Examples\\Blog\\Schema;\n\nclass DataProvider\n{\n    public static function getPost($id)\n    {\n        return [\n            \"id\"        =\u003e \"post-\" . $id,\n            \"title\"     =\u003e \"Post \" . $id . \" title\",\n            \"summary\"   =\u003e \"This new GraphQL library for PHP works really well\",\n            \"status\"    =\u003e 1,\n            \"likesCount\" =\u003e 2\n        ];\n    }\n\n    public static function getBanner($id)\n    {\n        return [\n            'id'        =\u003e \"banner-\" . $id,\n            'title'     =\u003e \"Banner \" . $id,\n            'imageLink' =\u003e \"banner\" . $id . \".jpg\"\n        ];\n    }\n}\n```\n\nNow, we're ready to update our Schema and include `ContentBlockUnion` into it.\nAs we're getting our schema bigger we'd like to extract it to a separate file as well:\n```php\n\u003c?php\n/**\n * BlogSchema.php\n */\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Config\\Schema\\SchemaConfig;\nuse Youshido\\GraphQL\\Schema\\AbstractSchema;\nuse Youshido\\GraphQL\\Type\\ListType\\ListType;\n\nclass BlogSchema extends AbstractSchema\n{\n    public function build(SchemaConfig $config)\n    {\n        $config-\u003egetQuery()-\u003eaddFields([\n            new LatestPostField(),\n            'randomBanner'     =\u003e [\n                'type'    =\u003e new BannerType(),\n                'resolve' =\u003e function () {\n                    return DataProvider::getBanner(rand(1, 10));\n                }\n            ],\n            'pageContentUnion' =\u003e [\n                'type'    =\u003e new ListType(new ContentBlockUnion()),\n                'resolve' =\u003e function () {\n                    return [DataProvider::getPost(1), DataProvider::getBanner(1)];\n                }\n            ]\n        ]);\n        $config-\u003egetMutation()-\u003eaddFields([\n            new LikePostField()\n        ]);\n    }\n\n}\n```\nHaving this separate schema file you should update your `index.php` to look like this:\n```php\n\u003c?php\n\nnamespace Examples\\Blog;\n\nuse Examples\\Blog\\Schema\\BlogSchema;\nuse Youshido\\GraphQL\\Execution\\Processor;\n\nrequire_once __DIR__ . '/vendor/autoload.php';\nrequire_once __DIR__ . '/Schema/PostType.php';\nrequire_once __DIR__ . '/Schema/LatestPostField.php';\nrequire_once __DIR__ . '/Schema/ContentBlockInterface.php';\nrequire_once __DIR__ . '/Schema/PostStatus.php';\nrequire_once __DIR__ . '/Schema/LikePostField.php';\nrequire_once __DIR__ . '/Schema/BlogSchema.php';\nrequire_once __DIR__ . '/Schema/ContentBlockUnion.php';\nrequire_once __DIR__ . '/Schema/BannerType.php';\nrequire_once __DIR__ . '/Schema/DataProvider.php';\n\n$processor = new Processor(new BlogSchema());\n$payload  = '{ pageContentUnion { ... on Post { title } ... on Banner { title, imageLink } } }';\n\n\n$processor-\u003eprocessPayload($payload);\necho json_encode($processor-\u003egetResponseData()) . \"\\n\";\n```\n\nDue to the GraphQL syntax you have to specify fields for each type of object you're getting in the union request, if you're not familiar with it read more at [official documentation](https://facebook.github.io/graphql/#sec-Unions)\nIf everything was done right you should see the following response:\n```js\n{\"data\":{\"pageContentUnion\":[\n  {\"title\":\"Post 1 title\"},\n  {\"title\":\"Banner 1\",\"imageLink\":\"banner1.jpg\"}\n]}}\n```\nAlso, you might want to check out how to use [GraphiQL tool](#graphiql-tool) to get a better visualization of what you're doing here.\n\n### Lists\n\nAs you've seen in the previous example `ListType` is used to create a list of any items that are or extend GraphQL type.\nList type can be also created by using `InterfaceType` as an item which gives you flexibility in defining your schema.\nLet's go ahead and add `ListType` field to our BlogSchema.\n```php\n\u003c?php\n/**\n * BlogSchema.php\n */\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Config\\Schema\\SchemaConfig;\nuse Youshido\\GraphQL\\Schema\\AbstractSchema;\nuse Youshido\\GraphQL\\Type\\ListType\\ListType;\n\nclass BlogSchema extends AbstractSchema\n{\n    public function build(SchemaConfig $config)\n    {\n        $config-\u003egetQuery()-\u003eaddFields([\n            new LatestPostField(),\n            'randomBanner'     =\u003e [\n                'type'    =\u003e new BannerType(),\n                'resolve' =\u003e function () {\n                    return DataProvider::getBanner(rand(1, 10));\n                }\n            ],\n            'pageContentUnion' =\u003e [\n                'type'    =\u003e new ListType(new ContentBlockUnion()),\n                'resolve' =\u003e function () {\n                    return [DataProvider::getPost(1), DataProvider::getBanner(1)];\n                }\n            ],\n            'pageContentInterface' =\u003e [\n                'type'    =\u003e new ListType(new ContentBlockInterface()),\n                'resolve' =\u003e function () {\n                    return [DataProvider::getPost(2), DataProvider::getBanner(3)];\n                }\n            ]\n        ]);\n        $config-\u003egetMutation()-\u003eaddFields([\n            new LikePostField()\n        ]);\n    }\n\n}\n```\n\nWe've added a `pageContentInterface` field that have a `ListType` of `ContentBlockInterface`.  \nResolve function returns list which consists of one `Post` and one `Banner`.\nTo test it we'll modify our payload to the following one:\n```php\n\u003c?php\n$payload  = '{ pageContentInterface { title} }';\n```\nBe aware, because `BannerType` doesn't implement `ContentBlockInterface` you would get an error:\n```js\n{ \"errors\": [ \"message\": \"Type Banner does not implement ContentBlockInterface\" } ]}\n```\nTo fix this we just need to add `ContentBlockInterface` by implementing `getInterfaces` method and adding the proper field definitions to our `BannerType`:\n```php\n\u003c?php\n/**\n * BannerType.php\n */\n\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Config\\TypeConfigInterface;\nuse Youshido\\GraphQL\\Type\\NonNullType;\nuse Youshido\\GraphQL\\Type\\Object\\AbstractObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass BannerType extends AbstractObjectType\n{\n    public function build($config)\n    {\n        $config\n            -\u003eaddField('title', new NonNullType(new StringType()))\n            -\u003eaddField('summary', new StringType())\n            -\u003eaddField('imageLink', new StringType());\n    }\n\n    public function getInterfaces()\n    {\n        return [new ContentBlockInterface()];\n    }\n}\n```\nSend the request again and you'll get a nice response with titles of the both Post and Banner:\n```js\n{\n  \"data\": {\n    \"pageContentInterface\":[\n      {\"title\":\"Post 2 title\"},\n      {\"title\":\"Banner 3\"}\n    ]\n  }\n}\n```\n\n### Input Objects\nSo far we've been working mostly on the requests that does not require you to send any kind of data other than a simple `Int`, but in real life you'll have a lot of requests (mutations) where you'll be sending to server all kind of forms – login, registration, create post and so on.\nIn order to properly handle and validate that data GraphQL type system provides an `InputObjectType` class.\n\u003e By default all the `Scalar` types are inputs but if you want to have a single more complicated input type you need to extend an `InputObjectType`.\n\nLet's develop a `PostInputType` that could be used to create a new Post in our system.\n```php\n\u003c?php\n/**\n * PostInputType.php\n */\n\nnamespace Examples\\Blog\\Schema;\n\nuse Youshido\\GraphQL\\Type\\Config\\InputTypeConfigInterface;\nuse Youshido\\GraphQL\\Type\\NonNullType;\nuse Youshido\\GraphQL\\Type\\InputObject\\AbstractInputObjectType;\nuse Youshido\\GraphQL\\Type\\Scalar\\StringType;\n\nclass PostInputType extends AbstractInputObjectType\n{\n\n    public function build($config)\n    {\n        $config\n            -\u003eaddField('title', new NonNullType(new StringType()))\n            -\u003eaddField('summary', new StringType());\n    }\n\n}\n```\n\nThis `InputType` could be used to create a new mutation (we can do it in the `BlogSchema::build` for testing):\n```php\n\u003c?php\n// BlogSchema-\u003ebuild() method\n$config-\u003egetMutation()-\u003eaddFields([\n    'likePost'   =\u003e new LikePost(),\n    'createPost' =\u003e [\n        'type'   =\u003e new PostType(),\n        'args' =\u003e [\n            'post'   =\u003e new PostInputType(),\n            'author' =\u003e new StringType()\n        ],\n        'resolve' =\u003e function($value, array $args, ResolveInfo $info) {\n            // code for creating a new post goes here\n            // we simple use our DataProvider for now\n            $post = DataProvider::getPost(10);\n            if (!empty($args['post']['title'])) $post['title'] = $args['post']['title'];\n            return $post;\n        }\n    ]\n]);\n```\n\nTry to execute the following mutation so you can see the result:\n```\nmutation {\n  createPost(author: \"Alex\", post: {title: \"Hey, this is my new post\", summary: \"my post\" }) {\n    title\n  }\n}\n```\nresult:\n```js\n{\"data\":{\"createPost\":{\"title\":\"Hey, this is my new post\"}}}\n```\n\u003e The best way to see the result of your queries/mutations and to inspect the Schema is to use a [GraphiQL tool](#graphiql-tool)\n\n### Non Null\n\n`NonNullType` is really simple to use – consider it as a wrapper that can ensure that your field / argument is required and being passed to the resolve function.\nWe have used `NonNullType` couple of times already so we'll just show you useful methods that that could be called on `NonNullType` objects:\n- `getNullableType()`\n- `getNamedType()`\n\nThese two can return you a type that was wrapped up in the `NonNullType` so you can get it's fields, arguments or name.\n\n## Building your schema\n\nIt's always a good idea to give you a heads up about any possible errors as soon as possible, better on the development stage.\nFor this purpose specifically we made a lot of Abstract classes that will force you to implement the right methods to reduce amount of errors or if you're lucky enough – to have no errors at all.\n\n### Abstract type classes\nIf you want to implement a new type consider extending the following classes:\n* AbstractType\n* AbstractScalarType\n* AbstractObjectType\n* AbstractMutationObjectType\n* AbstractInputObjectType\n* AbstractInterfaceType\n* AbstractEnumType\n* AbstractListType\n* AbstractUnionType\n* AbstractSchemaType\n\n### Mutation helper class\nYou can create a mutation by extending `AbstractObjectType` or by creating a new field of `ObjectType` inside your `Schema::build` method.\nIt is crucial for the class to have a `getType` method returning the actual OutputType of your mutation but it couldn't be implemented as abstract method, so we created a wrapper class called `AbstractMutationObjectType`.\nThis abstract class can help you to not forget about `OutputType` by forcing you to implement a method `getOutputType` that will eventually be used by internal `getType` method.\n\n## Useful information\n\nThis section will be updating on a regular basis with the useful links and references that might help you to quicker become a better GraphQL developer.\n\n### GraphiQL Tool\nTo improve our testing experience even more we suggest to start using GraphiQL client, that's included in our examples. It's a JavaScript GraphQL Schema Explorer.\nTo use it – run the `server.sh` from the `examples/02_blog/` folder and open the `examples/GraphiQL/index.html` file in your browser.\nYou'll see a nice looking editor that has an autocomplete function and contains all information about your current Schema on the right side in the Docs sidebar:\n![GraphiQL Interface](https://raw.githubusercontent.com/Youshido/GraphQL/master/examples/GraphiQL/screenshot.png)\n","funding_links":[],"categories":["Libraries","PHP"],"sub_categories":["PHP Libraries"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyoushido-php%2FGraphQL","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyoushido-php%2FGraphQL","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyoushido-php%2FGraphQL/lists"}