diff --git a/data-fetching/index.html b/data-fetching/index.html index e6f8c4c..b496106 100755 --- a/data-fetching/index.html +++ b/data-fetching/index.html @@ -357,7 +357,7 @@ for a field you simply override this default resolver.

Keep in mind that field resolver has precedence over default field resolver per type which in turn has precedence over default field resolver.

Solving N+1 Problem

-

Since: 9.0

+

Since: 0.9.0

One of the most annoying problems with data fetching is so-called N+1 problem.

Consider following GraphQL query:

{
@@ -379,7 +379,7 @@ when one batched query could be executed instead of 10 distinct queries.

MyUserBuffer::add($blogStory['authorId']); return new GraphQL\Deferred(function () use ($blogStory) { - MyUserBuffer::loadOnce(); + MyUserBuffer::loadBuffered(); return MyUserBuffer::get($blogStory['authorId']); }); } @@ -412,7 +412,7 @@ project.

In this example only one query will be executed for all story authors comparing to 20 queries in naive implementation.

Async PHP

-

Since: 9.0

+

Since: 0.9.0

If your project runs in environment that supports async operations (like HHVM, ReactPHP, Icicle.io, appserver.io PHP threads, etc) you can leverage the power of your platform to resolve fields asynchronously.

@@ -429,7 +429,6 @@ the power of your platform to resolve fields asynchronously.

To integrate other platform - implement GraphQL\Executor\Promise\PromiseAdapter interface.

diff --git a/index.html b/index.html index cd2d24f..1e8a4fa 100755 --- a/index.html +++ b/index.html @@ -303,5 +303,5 @@ Schema Language parser.

diff --git a/mkdocs/search_index.json b/mkdocs/search_index.json index 61d698d..7de0e76 100755 --- a/mkdocs/search_index.json +++ b/mkdocs/search_index.json @@ -317,7 +317,7 @@ }, { "location": "/data-fetching/", - "text": "Overview\n\n\nGraphQL is data-storage agnostic. You can use any underlying data storage engine, including SQL or NoSQL database, \nplain files or in-memory data structures.\n\n\nIn order to convert GraphQL query to PHP array \ngraphql-php\n traverses query fields (using depth-first algorithm) and \nruns special \nresolve\n function on each field. This \nresolve\n function is provided by you as a part of \n\nfield definition\n.\n\n\nResult returned by \nresolve\n function is directly included in response (for scalars and enums)\nor passed down to nested fields (for objects).\n\n\nLet's walk through an example. Consider following GraphQL query:\n\n\n{\n lastStory {\n title\n author {\n name\n }\n }\n}\n\n\n\n\nWe need Schema that can fulfill it. On the very top level Schema contains Query type:\n\n\n$queryType = new ObjectType([\n 'name' =\n 'Query',\n 'fields' =\n [\n\n 'lastStory' =\n [\n 'type' =\n $blogStoryType,\n 'resolve' =\n function() {\n return [\n 'id' =\n 1,\n 'title' =\n 'Example blog post',\n 'authorId' =\n 1\n ];\n }\n ]\n\n ]\n]);\n\n\n\n\nAs we see field \nlastStory\n has \nresolve\n function that is responsible for fetching data.\n\n\nIn our example we simply return array value, but in real-world application you would query\nyour database/cache/search index and return result.\n\n\nSince \nlastStory\n is of complex type \nBlogStory\n this result is passed down to fields of this type:\n\n\n$blogStoryType = new ObjectType([\n 'name' =\n 'BlogStory',\n 'fields' =\n [\n\n 'author' =\n [\n 'type' =\n $userType,\n 'resolve' =\n function($blogStory) {\n $users = [\n 1 =\n [\n 'id' =\n 1,\n 'name' =\n 'Smith'\n ],\n 2 =\n [\n 'id' =\n 2,\n 'name' =\n 'Anderson'\n ]\n ];\n return $users[$blogStory['authorId']];\n }\n ],\n\n 'title' =\n [\n 'type' =\n Type::string()\n ]\n\n ]\n]);\n\n\n\n\nHere \n$blogStory\n is the array returned by \nlastStory\n field above. \n\n\nAgain: in real-world applications you would fetch user data from datastore by \nauthorId\n and return it.\nAlso note that you don't have to return arrays. You can return any value, \ngraphql-php\n will pass it untouched\nto nested resolvers.\n\n\nBut then the question appears - field \ntitle\n has no \nresolve\n option. How is it resolved?\n\n\nThe answer is: there is default resolver for all fields. When you define your own \nresolve\n function\nfor a field you simply override this default resolver.\n\n\nDefault Field Resolver\n\n\ngraphql-php\n provides following default field resolver:\n\n\nfunction defaultFieldResolver($source, $args, $context, ResolveInfo $info)\n{\n $fieldName = $info-\nfieldName;\n $property = null;\n\n if (is_array($source) || $source instanceof \\ArrayAccess) {\n if (isset($source[$fieldName])) {\n $property = $source[$fieldName];\n }\n } else if (is_object($source)) {\n if (isset($source-\n{$fieldName})) {\n $property = $source-\n{$fieldName};\n }\n }\n\n return $property instanceof \\Closure ? $property($source, $args, $context) : $property;\n}\n\n\n\n\nAs you see it returns value by key (for arrays) or property (for objects). If value is not set - it returns \nnull\n.\n\n\nTo override default resolver - use:\n\n\nGraphQL\\GraphQL::setDefaultFieldResolver($myResolverCallback);\n\n\n\n\nDefault Field Resolver per Type\n\n\nSometimes it might be convenient to set default field resolver per type. You can do so by providing\n\nresolveField option in type config\n. For example:\n\n\n$userType = new ObjectType([\n 'name' =\n 'User',\n 'fields' =\n [\n\n 'name' =\n Type::string(),\n 'email' =\n Type::string()\n\n ],\n 'resolveField' =\n function(User $user, $args, $context, ResolveInfo $info) {\n switch ($info-\nfieldName) {\n case 'name':\n return $user-\ngetName();\n case 'email':\n return $user-\ngetEmail();\n default:\n return null;\n }\n }\n]);\n\n\n\n\nKeep in mind that \nfield resolver\n has precedence over \ndefault field resolver per type\n which in turn\n has precedence over \ndefault field resolver\n.\n\n\nSolving N+1 Problem\n\n\nSince: 9.0\n\n\nOne of the most annoying problems with data fetching is so-called \nN+1 problem\n.\n\n\nConsider following GraphQL query:\n\n\n{\n topStories(limit: 10) {\n title\n author {\n name\n email\n }\n }\n}\n\n\n\n\nNaive field resolution process would require up to 10 calls to underlying data store to fetch authors for all 10 stories.\n\n\ngraphql-php\n provides tools to mitigate this problem: it allows you to defer actual field resolution to later stage \nwhen one batched query could be executed instead of 10 distinct queries.\n\n\nHere is an example of \nBlogStory\n resolver for field \nauthor\n that uses deferring:\n\n\n'resolve' =\n function($blogStory) {\n MyUserBuffer::add($blogStory['authorId']);\n\n return new GraphQL\\Deferred(function () use ($blogStory) {\n MyUserBuffer::loadOnce();\n return MyUserBuffer::get($blogStory['authorId']);\n });\n}\n\n\n\n\nIn this example we fill up buffer with 10 author ids first. Then \ngraphql-php\n continues \nresolving other non-deferred fields until there are none of them left.\n\n\nAfter that it calls \nClosures\n wrapped by \nGraphQL\\Deferred\n which in turn load all buffered \nids once (using SQL IN(?), Redis MGET or other similar tools) and return final field value.\n\n\nOriginally this approach was advocated by Facebook in their \nDataloader\n\nproject.\n\n\nThis solution enables very interesting optimizations at no cost. Consider following query:\n\n\n{\n topStories(limit: 10) {\n author {\n email\n }\n }\n category {\n stories(limit: 10) {\n author {\n email\n }\n }\n }\n}\n\n\n\n\nEven if \nauthor\n field is located on different levels of query - it can be buffered in the same buffer.\nIn this example only one query will be executed for all story authors comparing to 20 queries\nin naive implementation.\n\n\nAsync PHP\n\n\nSince: 9.0\n\n\nIf your project runs in environment that supports async operations \n(like \nHHVM\n, \nReactPHP\n, \nIcicle.io\n, \nappserver.io\n \nPHP threads\n, etc) you can leverage\nthe power of your platform to resolve fields asynchronously.\n\n\nThe only requirement: your platform must support the concept of Promises compatible with\n\nPromises A+\n specification.\n\n\nTo enable async support - set adapter for promises:\n\n\nGraphQL\\GraphQL::setPromiseAdapter($adapter);\n\n\n\n\nWhere \n$adapter\n is an instance of class implementing \nGraphQL\\Executor\\Promise\\PromiseAdapter\n interface.\n\n\nThen in your \nresolve\n functions you should return \nPromises\n of your platform instead of \n\nGraphQL\\Deferred\n instances.\n\n\nPlatforms supported out of the box:\n\n\n\n\nReactPHP\n (requires \nreact/promise\n as composer dependency):\n \nGraphQL\\GraphQL::setPromiseAdapter(new GraphQL\\Executor\\Promise\\Adapter\\ReactPromiseAdapter());\n\n\nHHVM: TODO\n\n\n\n\nTo integrate other platform - implement \nGraphQL\\Executor\\Promise\\PromiseAdapter\n interface.", + "text": "Overview\n\n\nGraphQL is data-storage agnostic. You can use any underlying data storage engine, including SQL or NoSQL database, \nplain files or in-memory data structures.\n\n\nIn order to convert GraphQL query to PHP array \ngraphql-php\n traverses query fields (using depth-first algorithm) and \nruns special \nresolve\n function on each field. This \nresolve\n function is provided by you as a part of \n\nfield definition\n.\n\n\nResult returned by \nresolve\n function is directly included in response (for scalars and enums)\nor passed down to nested fields (for objects).\n\n\nLet's walk through an example. Consider following GraphQL query:\n\n\n{\n lastStory {\n title\n author {\n name\n }\n }\n}\n\n\n\n\nWe need Schema that can fulfill it. On the very top level Schema contains Query type:\n\n\n$queryType = new ObjectType([\n 'name' =\n 'Query',\n 'fields' =\n [\n\n 'lastStory' =\n [\n 'type' =\n $blogStoryType,\n 'resolve' =\n function() {\n return [\n 'id' =\n 1,\n 'title' =\n 'Example blog post',\n 'authorId' =\n 1\n ];\n }\n ]\n\n ]\n]);\n\n\n\n\nAs we see field \nlastStory\n has \nresolve\n function that is responsible for fetching data.\n\n\nIn our example we simply return array value, but in real-world application you would query\nyour database/cache/search index and return result.\n\n\nSince \nlastStory\n is of complex type \nBlogStory\n this result is passed down to fields of this type:\n\n\n$blogStoryType = new ObjectType([\n 'name' =\n 'BlogStory',\n 'fields' =\n [\n\n 'author' =\n [\n 'type' =\n $userType,\n 'resolve' =\n function($blogStory) {\n $users = [\n 1 =\n [\n 'id' =\n 1,\n 'name' =\n 'Smith'\n ],\n 2 =\n [\n 'id' =\n 2,\n 'name' =\n 'Anderson'\n ]\n ];\n return $users[$blogStory['authorId']];\n }\n ],\n\n 'title' =\n [\n 'type' =\n Type::string()\n ]\n\n ]\n]);\n\n\n\n\nHere \n$blogStory\n is the array returned by \nlastStory\n field above. \n\n\nAgain: in real-world applications you would fetch user data from datastore by \nauthorId\n and return it.\nAlso note that you don't have to return arrays. You can return any value, \ngraphql-php\n will pass it untouched\nto nested resolvers.\n\n\nBut then the question appears - field \ntitle\n has no \nresolve\n option. How is it resolved?\n\n\nThe answer is: there is default resolver for all fields. When you define your own \nresolve\n function\nfor a field you simply override this default resolver.\n\n\nDefault Field Resolver\n\n\ngraphql-php\n provides following default field resolver:\n\n\nfunction defaultFieldResolver($source, $args, $context, ResolveInfo $info)\n{\n $fieldName = $info-\nfieldName;\n $property = null;\n\n if (is_array($source) || $source instanceof \\ArrayAccess) {\n if (isset($source[$fieldName])) {\n $property = $source[$fieldName];\n }\n } else if (is_object($source)) {\n if (isset($source-\n{$fieldName})) {\n $property = $source-\n{$fieldName};\n }\n }\n\n return $property instanceof \\Closure ? $property($source, $args, $context) : $property;\n}\n\n\n\n\nAs you see it returns value by key (for arrays) or property (for objects). If value is not set - it returns \nnull\n.\n\n\nTo override default resolver - use:\n\n\nGraphQL\\GraphQL::setDefaultFieldResolver($myResolverCallback);\n\n\n\n\nDefault Field Resolver per Type\n\n\nSometimes it might be convenient to set default field resolver per type. You can do so by providing\n\nresolveField option in type config\n. For example:\n\n\n$userType = new ObjectType([\n 'name' =\n 'User',\n 'fields' =\n [\n\n 'name' =\n Type::string(),\n 'email' =\n Type::string()\n\n ],\n 'resolveField' =\n function(User $user, $args, $context, ResolveInfo $info) {\n switch ($info-\nfieldName) {\n case 'name':\n return $user-\ngetName();\n case 'email':\n return $user-\ngetEmail();\n default:\n return null;\n }\n }\n]);\n\n\n\n\nKeep in mind that \nfield resolver\n has precedence over \ndefault field resolver per type\n which in turn\n has precedence over \ndefault field resolver\n.\n\n\nSolving N+1 Problem\n\n\nSince: 0.9.0\n\n\nOne of the most annoying problems with data fetching is so-called \nN+1 problem\n.\n\n\nConsider following GraphQL query:\n\n\n{\n topStories(limit: 10) {\n title\n author {\n name\n email\n }\n }\n}\n\n\n\n\nNaive field resolution process would require up to 10 calls to underlying data store to fetch authors for all 10 stories.\n\n\ngraphql-php\n provides tools to mitigate this problem: it allows you to defer actual field resolution to later stage \nwhen one batched query could be executed instead of 10 distinct queries.\n\n\nHere is an example of \nBlogStory\n resolver for field \nauthor\n that uses deferring:\n\n\n'resolve' =\n function($blogStory) {\n MyUserBuffer::add($blogStory['authorId']);\n\n return new GraphQL\\Deferred(function () use ($blogStory) {\n MyUserBuffer::loadBuffered();\n return MyUserBuffer::get($blogStory['authorId']);\n });\n}\n\n\n\n\nIn this example we fill up buffer with 10 author ids first. Then \ngraphql-php\n continues \nresolving other non-deferred fields until there are none of them left.\n\n\nAfter that it calls \nClosures\n wrapped by \nGraphQL\\Deferred\n which in turn load all buffered \nids once (using SQL IN(?), Redis MGET or other similar tools) and return final field value.\n\n\nOriginally this approach was advocated by Facebook in their \nDataloader\n\nproject.\n\n\nThis solution enables very interesting optimizations at no cost. Consider following query:\n\n\n{\n topStories(limit: 10) {\n author {\n email\n }\n }\n category {\n stories(limit: 10) {\n author {\n email\n }\n }\n }\n}\n\n\n\n\nEven if \nauthor\n field is located on different levels of query - it can be buffered in the same buffer.\nIn this example only one query will be executed for all story authors comparing to 20 queries\nin naive implementation.\n\n\nAsync PHP\n\n\nSince: 0.9.0\n\n\nIf your project runs in environment that supports async operations \n(like \nHHVM\n, \nReactPHP\n, \nIcicle.io\n, \nappserver.io\n \nPHP threads\n, etc) you can leverage\nthe power of your platform to resolve fields asynchronously.\n\n\nThe only requirement: your platform must support the concept of Promises compatible with\n\nPromises A+\n specification.\n\n\nTo enable async support - set adapter for promises:\n\n\nGraphQL\\GraphQL::setPromiseAdapter($adapter);\n\n\n\n\nWhere \n$adapter\n is an instance of class implementing \nGraphQL\\Executor\\Promise\\PromiseAdapter\n interface.\n\n\nThen in your \nresolve\n functions you should return \nPromises\n of your platform instead of \n\nGraphQL\\Deferred\n instances.\n\n\nPlatforms supported out of the box:\n\n\n\n\nReactPHP\n (requires \nreact/promise\n as composer dependency):\n \nGraphQL\\GraphQL::setPromiseAdapter(new GraphQL\\Executor\\Promise\\Adapter\\ReactPromiseAdapter());\n\n\n\n\nTo integrate other platform - implement \nGraphQL\\Executor\\Promise\\PromiseAdapter\n interface.", "title": "Fetching Data" }, { @@ -337,12 +337,12 @@ }, { "location": "/data-fetching/#solving-n1-problem", - "text": "Since: 9.0 One of the most annoying problems with data fetching is so-called N+1 problem . Consider following GraphQL query: {\n topStories(limit: 10) {\n title\n author {\n name\n email\n }\n }\n} Naive field resolution process would require up to 10 calls to underlying data store to fetch authors for all 10 stories. graphql-php provides tools to mitigate this problem: it allows you to defer actual field resolution to later stage \nwhen one batched query could be executed instead of 10 distinct queries. Here is an example of BlogStory resolver for field author that uses deferring: 'resolve' = function($blogStory) {\n MyUserBuffer::add($blogStory['authorId']);\n\n return new GraphQL\\Deferred(function () use ($blogStory) {\n MyUserBuffer::loadOnce();\n return MyUserBuffer::get($blogStory['authorId']);\n });\n} In this example we fill up buffer with 10 author ids first. Then graphql-php continues \nresolving other non-deferred fields until there are none of them left. After that it calls Closures wrapped by GraphQL\\Deferred which in turn load all buffered \nids once (using SQL IN(?), Redis MGET or other similar tools) and return final field value. Originally this approach was advocated by Facebook in their Dataloader \nproject. This solution enables very interesting optimizations at no cost. Consider following query: {\n topStories(limit: 10) {\n author {\n email\n }\n }\n category {\n stories(limit: 10) {\n author {\n email\n }\n }\n }\n} Even if author field is located on different levels of query - it can be buffered in the same buffer.\nIn this example only one query will be executed for all story authors comparing to 20 queries\nin naive implementation.", + "text": "Since: 0.9.0 One of the most annoying problems with data fetching is so-called N+1 problem . Consider following GraphQL query: {\n topStories(limit: 10) {\n title\n author {\n name\n email\n }\n }\n} Naive field resolution process would require up to 10 calls to underlying data store to fetch authors for all 10 stories. graphql-php provides tools to mitigate this problem: it allows you to defer actual field resolution to later stage \nwhen one batched query could be executed instead of 10 distinct queries. Here is an example of BlogStory resolver for field author that uses deferring: 'resolve' = function($blogStory) {\n MyUserBuffer::add($blogStory['authorId']);\n\n return new GraphQL\\Deferred(function () use ($blogStory) {\n MyUserBuffer::loadBuffered();\n return MyUserBuffer::get($blogStory['authorId']);\n });\n} In this example we fill up buffer with 10 author ids first. Then graphql-php continues \nresolving other non-deferred fields until there are none of them left. After that it calls Closures wrapped by GraphQL\\Deferred which in turn load all buffered \nids once (using SQL IN(?), Redis MGET or other similar tools) and return final field value. Originally this approach was advocated by Facebook in their Dataloader \nproject. This solution enables very interesting optimizations at no cost. Consider following query: {\n topStories(limit: 10) {\n author {\n email\n }\n }\n category {\n stories(limit: 10) {\n author {\n email\n }\n }\n }\n} Even if author field is located on different levels of query - it can be buffered in the same buffer.\nIn this example only one query will be executed for all story authors comparing to 20 queries\nin naive implementation.", "title": "Solving N+1 Problem" }, { "location": "/data-fetching/#async-php", - "text": "Since: 9.0 If your project runs in environment that supports async operations \n(like HHVM , ReactPHP , Icicle.io , appserver.io PHP threads , etc) you can leverage\nthe power of your platform to resolve fields asynchronously. The only requirement: your platform must support the concept of Promises compatible with Promises A+ specification. To enable async support - set adapter for promises: GraphQL\\GraphQL::setPromiseAdapter($adapter); Where $adapter is an instance of class implementing GraphQL\\Executor\\Promise\\PromiseAdapter interface. Then in your resolve functions you should return Promises of your platform instead of GraphQL\\Deferred instances. Platforms supported out of the box: ReactPHP (requires react/promise as composer dependency):\n GraphQL\\GraphQL::setPromiseAdapter(new GraphQL\\Executor\\Promise\\Adapter\\ReactPromiseAdapter()); HHVM: TODO To integrate other platform - implement GraphQL\\Executor\\Promise\\PromiseAdapter interface.", + "text": "Since: 0.9.0 If your project runs in environment that supports async operations \n(like HHVM , ReactPHP , Icicle.io , appserver.io PHP threads , etc) you can leverage\nthe power of your platform to resolve fields asynchronously. The only requirement: your platform must support the concept of Promises compatible with Promises A+ specification. To enable async support - set adapter for promises: GraphQL\\GraphQL::setPromiseAdapter($adapter); Where $adapter is an instance of class implementing GraphQL\\Executor\\Promise\\PromiseAdapter interface. Then in your resolve functions you should return Promises of your platform instead of GraphQL\\Deferred instances. Platforms supported out of the box: ReactPHP (requires react/promise as composer dependency):\n GraphQL\\GraphQL::setPromiseAdapter(new GraphQL\\Executor\\Promise\\Adapter\\ReactPromiseAdapter()); To integrate other platform - implement GraphQL\\Executor\\Promise\\PromiseAdapter interface.", "title": "Async PHP" }, { diff --git a/sitemap.xml b/sitemap.xml index 9bf8055..3684caa 100755 --- a/sitemap.xml +++ b/sitemap.xml @@ -4,7 +4,7 @@ None/ - 2016-12-14 + 2016-12-17 daily @@ -12,7 +12,7 @@ None/getting-started/ - 2016-12-14 + 2016-12-17 daily @@ -21,61 +21,61 @@ None/type-system/ - 2016-12-14 + 2016-12-17 daily None/type-system/object-types/ - 2016-12-14 + 2016-12-17 daily None/type-system/scalar-types/ - 2016-12-14 + 2016-12-17 daily None/type-system/enum-types/ - 2016-12-14 + 2016-12-17 daily None/type-system/lists-and-nonnulls/ - 2016-12-14 + 2016-12-17 daily None/type-system/interfaces/ - 2016-12-14 + 2016-12-17 daily None/type-system/unions/ - 2016-12-14 + 2016-12-17 daily None/type-system/input-types/ - 2016-12-14 + 2016-12-17 daily None/type-system/directives/ - 2016-12-14 + 2016-12-17 daily None/type-system/schema/ - 2016-12-14 + 2016-12-17 daily @@ -84,7 +84,7 @@ None/executing-queries/ - 2016-12-14 + 2016-12-17 daily @@ -92,7 +92,7 @@ None/data-fetching/ - 2016-12-14 + 2016-12-17 daily @@ -100,7 +100,7 @@ None/error-handling/ - 2016-12-14 + 2016-12-17 daily @@ -108,7 +108,7 @@ None/complementary-tools/ - 2016-12-14 + 2016-12-17 daily