ivome/graphql-relay-php

GraphQL Relay参考实现的一个PHP端口

v0.7.0 2023-10-20 15:43 UTC

This package is auto-updated.

Last update: 2024-09-20 17:27:28 UTC


README

这是一个库,允许使用graphql-php参考实现轻松创建符合Relay规范的GraphQL服务器。

Build Status Coverage Status

注意:该代码是从Facebook的原始graphql-relay js实现迁移过来,并对一些PHP相关部分进行了调整和扩展

当前状态

基本的辅助函数功能已就绪,包括测试。由于PHP的限制,异步功能尚未迁移。请参阅此处此处的讨论。

入门指南

需要了解GraphQL和graphql-php实现的基本知识,以便为该库提供上下文。

有关GraphQL的一般概述,请参阅README,该文档位于GraphQL规范

该库旨在与graphql-php的GraphQL服务器参考实现一起使用。

有关Relay兼容GraphQL服务器应提供的功能概述,请参阅GraphQL Relay规范,该规范位于Relay网站。概述描述了一系列示例,这些示例作为测试存在于该存储库中。一种开始使用此存储库的好方法是阅读该文档和库中相应的测试。

使用Relay库为graphql-php

通过composer安装此存储库

composer require ivome/graphql-relay-php

在构建graphql-php的架构时,可以使用提供的库函数来简化Relay模式的创建。

连接

提供辅助函数来构建GraphQL类型以及实现返回这些类型的字段的resolve方法。

  • Relay::connectionArgs返回当字段返回支持双向分页的连接类型时应提供的参数。
  • Relay::forwardConnectionArgs返回当字段返回仅支持正向分页的连接类型时应提供的参数。
  • Relay::backwardConnectionArgs返回当字段返回仅支持反向分页的连接类型时应提供的参数。
  • Relay::connectionDefinitions返回给定节点类型的一个connectionType及其关联的edgeType
  • Relay::edgeType返回一个新的edgeType
  • Relay::connectionType返回一个新的connectionType
  • Relay::connectionFromArray是一个辅助方法,它接受一个数组以及来自connectionArgs的参数,进行分页和过滤,并返回一个对象,该对象符合connectionTyperesolve函数期望的形状。
  • Relay::cursorForObjectInConnection 是一个辅助方法,它接受一个数组和成员对象,并返回一个用于突变负载的游标。

以下是从 测试模式 中这些方法的示例用法。

$shipConnection = Relay::connectionDefinitions([
    'nodeType' => $shipType
]);

// this could also be written as
//
// $shipEdge = Relay::edgeType([
//     'nodeType' => $shipType
// ]);
// $shipConnection = Relay::connectionType([
//     'nodeType' => $shipType,
//     'edgeType' => $shipEdge
// ]);

$factionType = new ObjectType([
    'name' => 'Faction',
    'description' => 'A faction in the Star Wars saga',
    'fields' => function() use ($shipConnection) {
        return [
            'id' => Relay::globalIdField(),
            'name' => [
                'type' => Type::string(),
                'description' => 'The name of the faction.'
            ],
            'ships' => [
                'type' => $shipConnection['connectionType'],
                'description' => 'The ships used by the faction.',
                'args' => Relay::connectionArgs(),
                'resolve' => function($faction, $args) {
                    // Map IDs from faction back to ships
                    $data = array_map(function($id) {
                        return StarWarsData::getShip($id);
                    }, $faction['ships']);
                    return Relay::connectionFromArray($data, $args);
                }
            ]
        ];
    },
    'interfaces' => [$nodeDefinition['nodeInterface']]
]);

这显示了向表示连接的 Faction 对象添加一个 ships 字段。它使用 connectionDefinitions({nodeType: shipType}) 创建连接类型,将 connectionArgs 作为此函数的参数,然后通过传递船舶数组和参数到 connectionFromArray 来实现解析函数。

对象标识

提供了构建节点 GraphQL 类型以及实现围绕本地 ID 的全局 ID 的辅助函数。

  • Relay::nodeDefinitions 返回对象可以实现的 Node 接口,并返回包含在查询类型中的 node 根字段。为了实现这一点,它接受一个将 ID 解析为对象的函数,并确定给定对象的类型。
  • Relay::toGlobalId 接受一个类型名称和特定于此类型名称的 ID,并返回一个在所有类型中都是唯一的“全局 ID”。
  • Relay::fromGlobalId 接受由 toGlobalID 创建的“全局 ID”,并返回用于创建它的类型名称和 ID。
  • Relay::globalIdField 创建节点上的 id 字段的配置。
  • Relay::pluralIdentifyingRootField 创建一个接受非 ID 标识符列表(如用户名)并映射到相应对象的字段。

以下是从 测试模式 中这些方法的示例用法。

$nodeDefinition = Relay::nodeDefinitions(
    // The ID fetcher definition
    function ($globalId) {
        $idComponents = Relay::fromGlobalId($globalId);
        if ($idComponents['type'] === 'Faction'){
            return StarWarsData::getFaction($idComponents['id']);
        } else if ($idComponents['type'] === 'Ship'){
            return StarWarsData::getShip($idComponents['id']);
        } else {
            return null;
        }
    },
    // Type resolver
    function ($object) {
        return isset($object['ships']) ? self::getFactionType() : self::getShipType();
    }
);

$factionType = new ObjectType([
    'name' => 'Faction',
    'description' => 'A faction in the Star Wars saga',
    'fields' => function() use ($shipConnection) {
        return [
            'id' => Relay::globalIdField(),
            'name' => [
                'type' => Type::string(),
                'description' => 'The name of the faction.'
            ],
            'ships' => [
                'type' => $shipConnection['connectionType'],
                'description' => 'The ships used by the faction.',
                'args' => Relay::connectionArgs(),
                'resolve' => function($faction, $args) {
                    // Map IDs from faction back to ships
                    $data = array_map(function($id) {
                        return StarWarsData::getShip($id);
                    }, $faction['ships']);
                    return Relay::connectionFromArray($data, $args);
                }
            ]
        ];
    },
    'interfaces' => [$nodeDefinition['nodeInterface']]
]);

$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => function () use ($nodeDefinition) {
        return [
            'node' => $nodeDefinition['nodeField']
        ];
    },
]);

这使用 Relay::nodeDefinitions 构建节点接口和 node 字段;它使用 fromGlobalId 在将 ID 映射到对象的函数实现中解析传递的 ID。然后使用 Relay::globalIdField 方法在 Faction 上创建 id 字段,这也确保实现了 nodeInterface。最后,它将 node 字段添加到查询类型中,使用由 Relay::nodeDefinitions 返回的 nodeField

突变

提供了一个辅助函数来构建具有单个输入和客户端突变 ID 的突变。

  • Relay::mutationWithClientMutationId 接受一个名称、输入字段、输出字段和一个突变方法,该方法将输入字段映射到输出字段,并在过程中执行突变。然后它创建并返回一个字段配置,该配置可以用作突变类型的顶级字段。

以下是从 测试模式 中这些方法的示例用法。

$shipMutation = Relay::mutationWithClientMutationId([
    'name' => 'IntroduceShip',
    'inputFields' => [
        'shipName' => [
            'type' => Type::nonNull(Type::string())
        ],
        'factionId' => [
            'type' => Type::nonNull(Type::id())
        ]
    ],
    'outputFields' => [
        'ship' => [
            'type' => $shipType,
            'resolve' => function ($payload) {
                return StarWarsData::getShip($payload['shipId']);
            }
        ],
        'faction' => [
            'type' => $factionType,
            'resolve' => function ($payload) {
                return StarWarsData::getFaction($payload['factionId']);
            }
        ]
    ],
    'mutateAndGetPayload' => function ($input) {
        $newShip = StarWarsData::createShip($input['shipName'], $input['factionId']);
        return [
            'shipId' => $newShip['id'],
            'factionId' => $input['factionId']
        ];
    }
]);

$mutationType = new ObjectType([
    'name' => 'Mutation',
    'fields' => function () use ($shipMutation) {
        return [
            'introduceShip' => $shipMutation
        ];
    }
]);

此代码创建了一个名为 IntroduceShip 的突变,它接受一个派系 ID 和一个船名作为输入。它输出相关的 FactionShip。然后 mutateAndGetPayload 获取一个具有每个输入字段属性的对象,通过构建新船执行突变,然后返回一个将由输出字段解析的对象。

然后我们的突变类型使用 Relay::mutationWithClientMutationId 返回的值创建 introduceShip 字段。

贡献

在克隆此存储库后,请运行以下命令以确保安装依赖项:

composer install

开发完成后,可以通过运行以下命令评估完整的测试套件:

bin/phpunit tests