rez1dent3/php-graphql-client

此包已弃用且不再维护。未建议替代包。

GraphQL 客户端和查询构建器。

1.14.0 2023-03-14 22:44 UTC

This package is not auto-updated.

Last update: 2024-09-19 01:31:16 UTC


README

Build Status Total Downloads Latest Stable Version License

这是一个用 PHP 编写的 GraphQL 客户端,它提供了非常简单但功能强大的查询生成类,使得与 GraphQL 服务器的交互变得非常简单。

使用方法

使用此包生成 GraphQL 查询主要有以下三种方法:

  1. 查询类:简单的类,映射到 GraphQL 查询。它设计用于轻松快速地操作查询。
  2. QueryBuilder 类:可以动态生成 Query 对象的构建类。它设计用于在查询以动态方式构建的情况下使用。
  3. PHP GraphQL-OQM:此包的扩展。它消除了编写任何 GraphQL 查询或参考 API 文档或语法的需要。它从 API 模式生成查询对象,通过 GraphQL 的内省公开声明,然后可以简单地与之交互。

安装

运行以下命令使用 composer 安装此包

$ composer require gmostafa/php-graphql-client

对象到查询映射扩展

要避免编写任何查询并仅与从您的 API 模式生成的 PHP 对象交互,请访问 PHP GraphQL OQM 仓库

查询示例

简单查询

$gql = (new Query('companies'))
    ->setSelectionSet(
        [
            'name',
            'serialNumber'
        ]
    );

此简单查询将检索显示公司名称和序列号的全部公司。

完整形式

前一个示例中提供的查询表示为“简写形式”。简写形式涉及编写较少的代码行,这加快了编写查询的过程。下面是前一个示例中相同查询的完整形式示例。

$gql = (new Query())
    ->setSelectionSet(
        [
            (new Query('companies'))
                ->setSelectionSet(
                    [
                        'name',
                        'serialNumber'
                    ]
                )
        ]
    );

如示例所示,简写形式更易于阅读和编写,通常比完整形式更受青睐。

除非查询不能以简写形式表示(只有一个情况,即我们想在同一个对象中运行多个查询),否则不应使用完整形式。

多个查询

$gql = (new Query())
    ->setSelectionSet(
        [
            (new Query('companies'))
            ->setSelectionSet(
                [
                    'name',
                    'serialNumber'
                ]
            ),
            (new Query('countries'))
            ->setSelectionSet(
                [
                    'name',
                    'code',
                ]
            )
        ]
    );

此查询检索所有公司和国家,并为每个国家显示一些数据字段。它基本上在一个查询对象包装中运行两个(或更多如果需要)独立的查询。

编写多个查询需要以完整形式编写查询对象,以将每个查询表示为父查询对象下的子字段。

嵌套查询

$gql = (new Query('companies'))
    ->setSelectionSet(
        [
            'name',
            'serialNumber',
            (new Query('branches'))
                ->setSelectionSet(
                    [
                        'address',
                        (new Query('contracts'))
                            ->setSelectionSet(['date'])
                    ]
                )
        ]
    );

此查询更为复杂,不仅检索标量字段,还检索对象字段。此查询返回所有公司,显示它们的名称、序列号,并为每个公司,显示其所有分支,显示分支地址,并为每个地址检索与此地址绑定的所有合同,显示它们的日期。

带参数的查询

$gql = (new Query('companies'))
    ->setArguments(['name' => 'Tech Co.', 'first' => 3])
    ->setSelectionSet(
        [
            'name',
            'serialNumber'
        ]
    );

此查询不是通过添加参数来检索所有公司。此查询将检索名称为“Tech Co.”的前3家公司,显示它们的名称和序列号。

带数组参数的查询

$gql = (new Query('companies'))
    ->setArguments(['serialNumbers' => [159, 260, 371]])
    ->setSelectionSet(
        [
            'name',
            'serialNumber'
        ]
    );

此查询是参数查询的特殊情况。在此示例中,查询将仅检索序列号为159、260或371的公司,显示名称和序列号。

带输入对象参数的查询

$gql = (new Query('companies'))
    ->setArguments(['filter' => new RawObject('{name_starts_with: "Face"}')])
    ->setSelectionSet(
        [
            'name',
            'serialNumber'
        ]
    );

此查询是参数查询的另一个特殊情况。在此示例中,我们设置了一个具有一些值的自定义输入对象“filter”以限制返回的公司。我们将“filter”设置为“name_starts_with”并赋予值“Face”。此查询将仅检索名称以“Face”短语开头的公司。

正在构建的RawObject类用于将字符串原样注入查询中。输入到RawObject构造函数中的任何字符串都将原样放入查询中,而不会进行查询类通常执行的任何自定义格式化。

包含变量的查询

$gql = (new Query('companies'))
    ->setVariables(
        [
            new Variable('name', 'String', true),
            new Variable('limit', 'Int', false, 5)
        ]
    )
    ->setArguments(['name' => '$name', 'first' => '$limit'])
    ->setSelectionSet(
        [
            'name',
            'serialNumber'
        ]
    );

此查询展示了如何使用此包中的变量来允许由GraphQL标准实现的动态请求。

变量类

变量类是一个不可变的类,它代表GraphQL标准中的一个变量。它的构造函数接收4个参数

  • name:代表变量名
  • type:代表根据GraphQL服务器模式表示的变量类型
  • isRequired(可选):代表变量是否必需,默认为false
  • defaultValue(可选):代表分配给变量的默认值。只有在isRequired参数设置为false时,才会考虑默认值。

使用别名

$gql = (new Query())
    ->setSelectionSet(
        [
            (new Query('companies', 'TechCo'))
                ->setArguments(['name' => 'Tech Co.'])
                ->setSelectionSet(
                    [
                        'name',
                        'serialNumber'
                    ]
                ),
            (new Query('companies', 'AnotherTechCo'))
                ->setArguments(['name' => 'A.N. Other Tech Co.'])
                ->setSelectionSet(
                    [
                        'name',
                        'serialNumber'
                    ]
                )
        ]
    );

当需要多次以不同参数检索相同对象时,可以在查询构造函数的第二个参数中设置别名。

$gql = (new Query('companies'))
    ->setAlias('CompanyAlias')
    ->setSelectionSet(
        [
            'name',
            'serialNumber'
        ]
    );

别名也可以通过setter方法设置。

使用接口:包含内联片段的查询

当查询返回接口类型字段时,可能需要使用内联片段来访问底层具体类型上的数据。

此示例展示了如何使用此包生成内联片段

$gql = new Query('companies');
$gql->setSelectionSet(
    [
        'serialNumber',
        'name',
        (new InlineFragment('PrivateCompany'))
            ->setSelectionSet(
                [
                    'boardMembers',
                    'shareholders',
                ]
            ),
    ]
);

查询构建器

QueryBuilder类可以用来动态构建查询对象,这在某些情况下可能很有用。它的工作方式与Query类非常相似,但查询构建被划分为步骤。

这就是如何使用QueryBuilder创建“包含输入对象参数的查询”示例

$builder = (new QueryBuilder('companies'))
    ->setVariable('namePrefix', 'String', true)
    ->setArgument('filter', new RawObject('{name_starts_with: $namePrefix}'))
    ->selectField('name')
    ->selectField('serialNumber');
$gql = $builder->getQuery();

与Query类一样,可以使用第二个构造函数参数设置别名。

$builder = (new QueryBuilder('companies', 'CompanyAlias'))
    ->selectField('name')
    ->selectField('serialNumber');

$gql = $builder->getQuery();

或者通过setter方法

$builder = (new QueryBuilder('companies'))
    ->setAlias('CompanyAlias')
    ->selectField('name')
    ->selectField('serialNumber');

$gql = $builder->getQuery();

完整形式

与Query类一样,QueryBuilder类可以完整编写,以启用在单个查询构建器对象下编写多个查询。下面是一个使用QueryBuilder的完整形式的示例

$builder = (new QueryBuilder())
    ->setVariable('namePrefix', 'String', true)
    ->selectField(
        (new QueryBuilder('companies'))
            ->setArgument('filter', new RawObject('{name_starts_with: $namePrefix}'))
            ->selectField('name')
            ->selectField('serialNumber')
    )
    ->selectField(
        (new QueryBuilder('company'))
            ->setArgument('serialNumber', 123)
            ->selectField('name')
    );
$gql = $builder->getQuery();

此查询是前一个示例查询的扩展。它返回以名称前缀开头的所有公司,并返回具有值为123的serialNumber的公司,这两个结果都在同一个响应中。

构建客户端

可以通过提供GraphQL端点URL来轻松实例化Client对象。

Client构造函数还接收一个可选的"authorizationHeaders"数组,可用于向发送到GraphQL服务器的所有请求添加授权头。

示例

$client = new Client(
    'http://api.graphql.com',
    ['Authorization' => 'Basic xyz']
);

Client构造函数还接收一个可选的"httpOptions"数组,该数组将覆盖"authorizationHeaders",可用于添加自定义Guzzle HTTP客户端请求选项

示例

$client = new Client(
    'http://api.graphql.com',
    [],
    [ 
        'connect_timeout' => 5,
        'timeout' => 5,
        'headers' => [
            'Authorization' => 'Basic xyz'
            'User-Agent' => 'testing/1.0',
        ],
        'proxy' => [
                'http'  => 'tcp://localhost:8125', // Use this proxy with "http"
                'https' => 'tcp://localhost:9124', // Use this proxy with "https",
                'no' => ['.mit.edu', 'foo.com']    // Don't use a proxy with these
        ],
        'cert' => ['/path/server.pem', 'password']
        ...
    ]
);

可以使用实现了PSR-18接口的自己的预配置HTTP客户端。

示例

$client = new Client(
    'http://api.graphql.com',
    [],
    [],
    $myHttpClient
);

运行查询

结果格式化

使用GraphQL客户端运行查询并以对象结构获取结果

$results = $client->runQuery($gql);
$results->getData()->companies[0]->branches;

或以数组结构获取结果

$results = $client->runQuery($gql, true);
$results->getData()['companies'][1]['branches']['address'];

将变量传递给查询

运行包含变量的查询需要传递一个关联数组到runQuery方法,该数组将变量名(键)映射到变量值(值)。以下是一个示例

$gql = (new Query('companies'))
    ->setVariables(
        [
            new Variable('name', 'String', true),
            new Variable('limit', 'Int', false, 5)
        ]
    )
    ->setArguments(['name' => '$name', 'first' => '$limit'])
    ->setSelectionSet(
        [
            'name',
            'serialNumber'
        ]
    );
$variablesArray = ['name' => 'Tech Co.', 'first' => 5];
$results = $client->runQuery($gql, true, $variablesArray);

变更

变更遵循GraphQL中查询的相同规则,它们选择返回对象上的字段,接收参数,并且可以有子字段。

以下是如何构建和运行变更的示例

$mutation = (new Mutation('createCompany'))
    ->setArguments(['companyObject' => new RawObject('{name: "Trial Company", employees: 200}')])
    ->setSelectionSet(
        [
            '_id',
            'name',
            'serialNumber',
        ]
    );
$results = $client->runQuery($mutation);

可以像运行查询一样运行变更。

包含变量的变更示例

变异可以像查询一样使用相同的变量。以下是一个如何使用变量动态地将输入对象传递给GraphQL服务器的示例

$mutation = (new Mutation('createCompany'))
    ->setVariables([new Variable('company', 'CompanyInputObject', true)])
    ->setArguments(['companyObject' => '$company']);

$variables = ['company' => ['name' => 'Tech Company', 'type' => 'Testing', 'size' => 'Medium']];
$client->runQuery(
    $mutation, true, $variables
);

以下是与变异一起传递的变量结果

mutation($company: CompanyInputObject!) {
  createCompany(companyObject: $company)
}
{"company":{"name":"Tech Company","type":"Testing","size":"Medium"}}

实时API示例

GraphQL Pokemon是一个非常酷的公共GraphQL API,可以用来检索宝可梦数据。该API在互联网上公开可用,我们将用它来演示这个客户端的能力。

GitHub仓库链接: https://github.com/lucasbento/graphql-pokemon

API链接: https://graphql-pokemon.now.sh/

此查询可以检索任何宝可梦的进化及其攻击

query($name: String!) {
  pokemon(name: $name) {
    id
    number
    name
    evolutions {
      id
      number
      name
      weight {
        minimum
        maximum
      }
      attacks {
        fast {
          name
          type
          damage
        }
      }
    }
  }
}

这就是如何使用查询类编写此查询并在客户端中运行的示例

$client = new Client(
    'https://graphql-pokemon.now.sh/'
);
$gql = (new Query('pokemon'))
    ->setVariables([new Variable('name', 'String', true)])
    ->setArguments(['name' => '$name'])
    ->setSelectionSet(
        [
            'id',
            'number',
            'name',
            (new Query('evolutions'))
                ->setSelectionSet(
                    [
                        'id',
                        'number',
                        'name',
                        (new Query('attacks'))
                            ->setSelectionSet(
                                [
                                    (new Query('fast'))
                                        ->setSelectionSet(
                                            [
                                                'name',
                                                'type',
                                                'damage',
                                            ]
                                        )
                                ]
                            )
                    ]
                )
        ]
    );
try {
    $name = readline('Enter pokemon name: ');
    $results = $client->runQuery($gql, true, ['name' => $name]);
}
catch (QueryError $exception) {
    print_r($exception->getErrorDetails());
    exit;
}
print_r($results->getData()['pokemon']);

或者,这是如何使用QueryBuilder类生成此查询的示例

$client = new Client(
    'https://graphql-pokemon.now.sh/'
);
$builder = (new QueryBuilder('pokemon'))
    ->setVariable('name', 'String', true)
    ->setArgument('name', '$name')
    ->selectField('id')
    ->selectField('number')
    ->selectField('name')
    ->selectField(
        (new QueryBuilder('evolutions'))
            ->selectField('id')
            ->selectField('name')
            ->selectField('number')
            ->selectField(
                (new QueryBuilder('attacks'))
                    ->selectField(
                        (new QueryBuilder('fast'))
                            ->selectField('name')
                            ->selectField('type')
                            ->selectField('damage')
                    )
            )
    );
try {
    $name = readline('Enter pokemon name: ');
    $results = $client->runQuery($builder, true, ['name' => $name]);
}
catch (QueryError $exception) {
    print_r($exception->getErrorDetails());
    exit;
}
print_r($results->getData()['pokemon']);

执行原始查询

虽然这不是这个包的主要目标,但它支持使用Client类中的runRawQuery方法运行原始字符串查询,就像任何其他客户端一样。以下是如何使用它的示例

$gql = <<<QUERY
query {
    pokemon(name: "Pikachu") {
        id
        number
        name
        attacks {
            special {
                name
                type
                damage
            }
        }
    }
}
QUERY;

$results = $client->runRawQuery($gql);