From 1be5f471de7bce661f1db18c78bbcf9371dfefa5 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Fri, 5 Feb 2021 11:19:08 +1300 Subject: [PATCH] DOCS Resolver signature and context provider examples --- .../02_building_a_custom_query.md | 52 +++++++++++++++++++ .../06_extending/adding_middleware.md | 30 ++++++++--- 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/docs/en/02_Developer_Guides/19_GraphQL/03_working_with_generic_types/02_building_a_custom_query.md b/docs/en/02_Developer_Guides/19_GraphQL/03_working_with_generic_types/02_building_a_custom_query.md index c629c7a17..382fbe5e7 100644 --- a/docs/en/02_Developer_Guides/19_GraphQL/03_working_with_generic_types/02_building_a_custom_query.md +++ b/docs/en/02_Developer_Guides/19_GraphQL/03_working_with_generic_types/02_building_a_custom_query.md @@ -114,6 +114,58 @@ don't apply in this context. Most importantly, this means you need to implement your own `canView()` checks. [/notice] +## Resolver Method Arguments + +A resolver is executed in a particular query context, +which is passed into the method as arguments. + + * `$value`: An optional `mixed` value of the parent in your data graph. + Defaults to `null` on the root level, but can be useful to retrieve the object + when writing field-specific resolvers (see [Resolver Discovery](resolver_discovery)) + * `$args`: An array of optional arguments for this field (which is different from the [Query Variables](https://graphql.org/learn/queries/#variables)) + * `$context`: An arbitrary array which holds information shared between resolvers. + Use implementors of `SilverStripe\GraphQL\Schema\Interfaces\ContextProvider` to get and set + data, rather than relying on the array keys directly. + * `$info`: Data structure containing useful information for the resolving process (e.g. the field name). + See [Fetching Data](http://webonyx.github.io/graphql-php/data-fetching/) in the underlying PHP library for details. + +## Using Context Providers + +The `$context` array can be useful to get access to the HTTP request, +retrieve the current member, or find out details about the schema. +You can use it through implementors of the `SilverStripe\GraphQL\Schema\Interfaces\ContextProvider` interface. +In the example below, we'll demonstrate how you could limit viewing the country code to +users with ADMIN permissions. + + +**app/src/Resolvers/MyResolver.php** +```php +use GraphQL\Type\Definition\ResolveInfo; +use SilverStripe\GraphQL\QueryHandler\UserContextProvider; +use SilverStripe\Security\Permission; + +class MyResolver +{ + public static function resolveCountries($value = null, array $args = [], array $context = [], ?ResolveInfo $info = null): array + { + $member = UserContextProvider::get($context); + $canViewCode = ($member && Permission::checkMember($member, 'ADMIN')); + $results = []; + $countries = Injector::inst()->get(Locales::class)->getCountries(); + foreach ($countries as $code => $name) { + $results[] = [ + 'code' => $canViewCode ? $code : '', + 'name' => $name + ]; + } + + return $results; + } +} +``` + +## Resolver Discovery + This is great, but as we write more and more queries for types with more and more fields, it's going to get awfully laborious mapping all these resolvers. Let's clean this up a bit by adding a bit of convention over configuration, and save ourselves a lot of time to boot. We can do diff --git a/docs/en/02_Developer_Guides/19_GraphQL/06_extending/adding_middleware.md b/docs/en/02_Developer_Guides/19_GraphQL/06_extending/adding_middleware.md index 08275c043..44afb3fe5 100644 --- a/docs/en/02_Developer_Guides/19_GraphQL/06_extending/adding_middleware.md +++ b/docs/en/02_Developer_Guides/19_GraphQL/06_extending/adding_middleware.md @@ -30,26 +30,39 @@ The middleware API in the silverstripe-graphql module is separate from other com APIs in Silverstripe CMS, such as HTTPMiddleware. [/notice] -The signature for middleware is pretty simple: +The signature for middleware looks like this: ```php -public function process(array $params, callable $next) +public function process(Schema $schema, $query, $context, $vars, callable $next) ``` -`$params` is an arbitrary array of data, much like an event object -passed to an event handler. The `$next` parameter refers to the next -middleware in the chain. + * `$schema`: The underlying [Schema](http://webonyx.github.io/graphql-php/type-system/schema/) object. + Useful to inspect whether types are defined in a schema. + * `$query`: The raw query string. + * `$context`: An arbitrary array which holds information shared between resolvers. + Use implementors of `SilverStripe\GraphQL\Schema\Interfaces\ContextProvider` to get and set + data, rather than relying on the array keys directly. + * `$vars`: An array of (optional) [Query Variables](https://graphql.org/learn/queries/#variables). + * `$next`: A callable referring to the next middleware in the chain Let's write a simple middleware that logs our queries as they come in. ```php +use SilverStripe\GraphQL\QueryHandler\UserContextProvider; +use GraphQL\Type\Schema; + class LoggingMiddleware implements Middleware { - public function process(array $params, callable $next) + public function process(Schema $schema, $query, $context, $vars, callable $next) { - $query = $params['query']; + $member = UserContextProvider::get($context); + Injector::inst()->get(LoggerInterface::class) - ->info('Query executed: ' . $query); + ->info(sprintf( + 'Query executed: %s by %s', + $query, + $member ? $member->Title : ''; + )); // Hand off execution to the next middleware return $next($params); @@ -67,3 +80,4 @@ Now we can register the middleware with our query handler: Middlewares: logging: '%$MyProject\Middleware\LoggingMiddleware' ``` +