Начало работы в graphql-php: как добавить функции распознавателя в схему из файла .graphql? - PullRequest
0 голосов
/ 13 мая 2018

Я совершенно новичок в 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/ гласят: «По умолчанию такая схема создается без каких-либо распознавателей. Для выполнения запроса к этой схеме мы должны полагаться на преобразователь полей по умолчанию и корневое значение». - но нет примера для этого.

Как добавить функции распознавателя для каждого из типов / полей?

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Этот подход работает без создания экземпляра сервера.В моем случае у меня уже есть сервер, и я могу читать данные 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;
    }
}
0 голосов
/ 24 августа 2018

Вот что я в итоге сделал ...

$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);

Мне кажется, что это довольно хакерски, и я, вероятно, должен отдать на аутсорсингпреобразователи в отдельные файлы, но это работает ... Все еще сбит с толку, что нет простых примеров для этой задачи, может быть, даже лучше, чем мое решение ...

...