一款用 PHP 编写的 GraphQL 客户端,提供非常简单但功能强大的查询生成器类,使与 GraphQL 服务器交互的过程变得非常简单。
使用此包生成 GraphQL 查询有 3 种主要方法:
Query
对象的构建器类。它的设计用于以动态方式构建查询的情况。运行以下命令以使用 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 '
]
);
此查询是参数查询的另一个特殊情况。在此示例中,我们设置一个自定义输入对象“过滤器”,其中包含一些值来限制返回的公司。我们将过滤器“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 标准启用的动态请求。
Variable 类是一个不可变类,表示 GraphQL 标准中的变量。它的构造函数接收 4 个参数:
$ 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 '
]
)
]
);
当需要使用不同参数多次检索同一对象时,可以在 Query 构造函数的第二个参数中设置别名。
$ 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 对象,这在某些情况下很有用。它的工作方式与 Query 类非常相似,但 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 ();
此查询是上一示例中查询的扩展。它返回以名称前缀开头的所有公司,并返回serialNumber
值为 123 的公司,两者都在同一响应中。
通过提供 GraphQL 端点 URL 可以轻松实例化 Client 对象。
Client 构造函数还接收一个可选的“authorizationHeaders”数组,该数组可用于向发送到 GraphQL 服务器的所有请求添加授权标头。
例子:
$ client = new Client (
' http://api.graphql.com ' ,
[ ' Authorization ' => ' Basic xyz ' ]
);
客户端构造函数还接收一个可选的“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 ' ]
. . .
]
);
可以使用您自己的预配置 HTTP 客户端来实现 PSR-18 接口。
例子:
$ 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"}}
GraphQL Pokemon 是一个非常酷的公共 GraphQL API,可用于检索 Pokemon 数据。该 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 );