【问题标题】:getting started in graphql-php: how to add resolver functions to schema from .graphql file?graphql-php 入门:如何将解析器函数从 .graphql 文件添加到模式?
【发布时间】:2018-10-23 09:27:36
【问题描述】:

我对 GraphQL 完全陌生,想尝试使用 graphql-php 来构建一个简单的 API 以开始使用。我目前正在阅读文档并尝试示例,但我一开始就卡住了。

我希望将我的架构存储在 schema.graphql 文件中,而不是手动构建它,所以我按照文档说明了如何做到这一点,它确实有效:

<?php
// graph-ql is installed via composer
require('../vendor/autoload.php');

use GraphQL\Language\Parser;
use GraphQL\Utils\BuildSchema;
use GraphQL\Utils\AST;
use GraphQL\GraphQL;

try {
    $cacheFilename = 'cached_schema.php';
    // caching, as recommended in the docs, is disabled for testing
    // if (!file_exists($cacheFilename)) {
        $document = Parser::parse(file_get_contents('./schema.graphql'));
        file_put_contents($cacheFilename, "<?php\nreturn " . var_export(AST::toArray($document), true) . ';');
    /*} else {
        $document = AST::fromArray(require $cacheFilename); // fromArray() is a lazy operation as well
    }*/

    $typeConfigDecorator = function($typeConfig, $typeDefinitionNode) {
        // In the docs, this function is just empty, but I needed to return the $typeConfig, otherwise I got an error
        return $typeConfig;
    };
    $schema = BuildSchema::build($document, $typeConfigDecorator);

    $context = (object)array();

    // this has been taken from one of the examples provided in the repo
    $rawInput = file_get_contents('php://input');
    $input = json_decode($rawInput, true);
    $query = $input['query'];
    $variableValues = isset($input['variables']) ? $input['variables'] : null;
    $rootValue = ['prefix' => 'You said: '];
    $result = GraphQL::executeQuery($schema, $query, $rootValue, $context, $variableValues);
    $output = $result->toArray();
} catch (\Exception $e) {
    $output = [
        'error' => [
            'message' => $e->getMessage()
        ]
    ];
}
header('Content-Type: application/json; charset=UTF-8');
echo json_encode($output);

这是我的schema.graphql 文件的样子:

schema {
    query: Query    
}

type Query {
    products: [Product!]!
}

type Product {
    id: ID!,
    type: ProductType
}

enum ProductType {
    HDRI,
    SEMISPHERICAL_HDRI,
    SOUND
}

我可以查询它例如

query {
  __schema {types{name}}
}

这将按预期返回元数据。但当然,现在我想查询实际的产品数据并从数据库中获取,为此我需要定义一个解析器函数。

http://webonyx.github.io/graphql-php/type-system/type-language/ 的文档指出:“默认情况下,创建此类架构时没有任何解析器。我们必须依赖默认字段解析器和根值才能针对此架构执行查询。” - 但是没有这样做的例子。

如何为每个类型/字段添加解析器函数?

【问题讨论】:

  • 您自己找到答案了吗?如果是这样,愿意在这里分享吗?谢谢!
  • 嗨@Seb,我在下面发布了一个答案。
  • 我找到了一种不同的方式,它也感觉很hackish,但它无需创建服务器即可工作,并将其添加为后代的答案(?)
  • 这对我的应用程序变得更加重要,因为出现了 graphql webpack 加载器。我重复了很多工作,将我的模式定义为一堆大的 PHP 数组,然后从大字符串构建我的客户端查询。我可以在buildSchema 服务器端加载.graphql 文件,在客户端加载import,从而确保定义保持同步。
  • 我发现 siler lib 有一个很好的方式来加载 graphql(加载架构,然后加载解析器),但它只是在 graphql-php 之上。您可以查看他们的代码以了解它是如何完成的——看起来他们经常使用GraphQL\Executor\Executor 来设置解析器。 github.com/leocavalcante/siler/blob/master/src/Graphql/…

标签: graphql graphql-php


【解决方案1】:

这就是我最终做的事情......

$rootResolver = array(
    'emptyCart' => function($root, $args, $context, $info) {
        global $rootResolver;
        initSession();
        $_SESSION['CART']->clear();
        return $rootResolver['getCart']($root, $args, $context, $info);
    },
    'addCartProduct' => function($root, $args, $context, $info) {
        global $rootResolver;

        ...

        return $rootResolver['getCart']($root, $args, $context, $info);
    },
    'removeCartProduct' => function($root, $args, $context, $info) {
        global $rootResolver;

        ...

        return $rootResolver['getCart']($root, $args, $context, $info);
    },
    'getCart' => function($root, $args, $context, $info) {
        initSession();
        return array(
            'count' => $_SESSION['CART']->quantity(),
            'total' => $_SESSION['CART']->total(),
            'products' => $_SESSION['CART']->getProductData()
        );
    },

然后在配置中

$config = ServerConfig::create()
    ->setSchema($schema)
    ->setRootValue($rootResolver)
    ->setContext($context)
    ->setDebug(DEBUG_MODE)
    ->setQueryBatching(true)
;

$server = new StandardServer($config);

对我来说,这感觉相当 hack-ish,我可能应该将解析器外包到单独的文件中,但它确实有效......仍然感到困惑的是,这个任务没有简单的例子,也许比我的解决方案更好...

【讨论】:

    【解决方案2】:

    这种方法无需实例化服务器即可工作。在我的例子中,我已经有一个服务器并且可以读取 HTTP 数据,我所需要的只是读取 GraphQL 模式并运行查询。首先,我从文件中读取架构:

            $schemaContent = // file_get_contents or whatever works for you
    
            $schemaDocument = GraphQL\Language\Parser::parse($schemaContent);
            $schemaBuilder = new GraphQL\Utils\BuildSchema($schemaDocument);
            $schema = $schemaBuilder->buildSchema();
    

    然后我通过自定义字段解析器执行查询:

            $fieldResolver = function() {
                return call_user_func_array([$this, 'defaultFieldResolver'], func_get_args());
            };
    
            $result = GraphQL\GraphQL::executeQuery(
                $schema,
                $query,        // this was grabbed from the HTTP post data
                null,
                $appContext,   // custom context
                $variables,    // this was grabbed from the HTTP post data
                null,
                $fieldResolver // HERE, custom field resolver
            );
    

    字段解析器如下所示:

    private static function defaultFieldResolver(
        $source,
        $args,
        $context,
        \GraphQL\Type\Definition\ResolveInfo $info
    ) {
        $fieldName = $info->fieldName;
        $parentType = $info->parentType->name;
    
        if ($source === NULL) {
            // this is the root value, return value depending on $fieldName
            // ...
        } else {
            // Depending on field type ($parentType), I call different field resolvers.
            // Since our system is big, we implemented a bootstrapping mechanism
            // so modules can register field resolvers in this class depending on field type
            // ...
    
            // If no field resolver was defined for this $parentType,
            // we just rely on the default field resolver provided by graphql-php (copy/paste).
            $fieldName = $info->fieldName;
            $property = null;
    
            if (is_array($source) || $source instanceof \ArrayAccess) {
                if (isset($source[$fieldName])) {
                    $property = $source[$fieldName];
                }
            } else if (is_object($source)) {
                if (isset($source->{$fieldName})) {
                    $property = $source->{$fieldName};
                }
            }
    
            return $property instanceof \Closure
                ? $property($source, $args, $context)
                : $property;
        }
    }
    

    【讨论】:

      【解决方案3】:

      我为此使用根值:

      <?php
      
      require("vendor/autoload.php") ;
      require("exemplo-graphql.php");
      require("Usuario.php");
      
      use GraphQL\GraphQL;
      use GraphQL\Type\Schema;
      use GraphQL\Utils\BuildSchema;
      
      $query = $_REQUEST['query'];
      
      $typeConfigDecorator = function($typeConfig, $typeDefinitionNode) {
          $name = $typeConfig['name'];
          // ... add missing options to $typeConfig based on type $name
          return $typeConfig;
      };
      
      $contents = file_get_contents('schema.graphql');
      $schema = BuildSchema::build($contents, $typeConfigDecorator);
      
      // $rawInput = file_get_contents('php://input');
      $input = json_decode($query, true);
      $query = $input['query'];
      $variableValues = isset($input['variables']) ? $input['variables'] : null;
      
      try {
          // $rootValue = ['prefix' => 'You said: '];
          $rootValue = [
              'usuario' => function($root, $args, $context, $info) {
                  $usuario = new Usuario();
                  $usuario->setNome("aqui tem um teste");
                  $usuario->setEmail("aqui tem um email");
                  return $usuario;
              },
              'echo' => function($root, $args, $context, $info) {
                  return "aqui tem um echooo";
              },
              'adicionarUsuario' => function ($root, $args, $context, $info) {
                  $usuario = new Usuario();
                  $usuario->setNome("aqui tem um teste");
                  $usuario->setEmail("aqui tem um email");
                  return $usuario;
              }
          ];
      
          $result = GraphQL::executeQuery($schema, $query, $rootValue, null,
              $variableValues);
      
          if ($result->errors) {
              $output = [
                  'errors' => [
                      [
                          'message' => $result->errors
                      ]
                  ]
          ];
          } else {
              $output = $result->toArray();
          }
      } catch (\Exception $e) {
          $output = [
              'errors' => [
                  [
                      'message' => $e->getMessage()
                  ]
              ]
          ];
      } 
      
      header('Content-Type: application/json');
      echo json_encode($output);
      

      【讨论】:

        猜你喜欢
        • 2019-02-08
        • 2020-07-30
        • 2020-07-28
        • 2020-04-05
        • 2020-08-19
        • 2020-09-28
        • 2018-03-15
        • 2019-10-24
        • 2019-07-25
        相关资源
        最近更新 更多