GraphQl
这是基于JavaScript参考实现的GraphQL规范的PHP实现。
相关项目
- DateTime标量
- 继电器支持
要求
- PHP版本> = 7.1
- EXT-MBSTRING
目录
- 安装
- 例子
- 创建模式
- 解析器注册表
- 解析器中间件
- 执行
- 查询
- 解析器
- N+1问题
- 变量
- 语境
- 标量
- 自定义标量
- 高级用法
- 一体化
- 拉拉维尔
安装
运行以下命令通过Composer安装软件包:
composer require digiaonline/graphql例子
这是一个简单的示例,该示例演示了如何从GraphQl架构文件中构建可执行模式,该文件包含以星球大战为主题的架构的架构定义语言(SDL)(有关架构定义本身,请参见下文)。在此示例中,我们使用该SDL来构建可执行模式,并使用它来查询英雄名称。该查询的结果是一个关联阵列,其结构类似于我们运行的查询。
use Digia GraphQL Language FileSourceBuilder ;
use function Digia GraphQL buildSchema ;
use function Digia GraphQL graphql ;
$ sourceBuilder = new FileSourceBuilder ( __DIR__ . ' /star-wars.graphqls ' );
$ schema = buildSchema ( $ sourceBuilder -> build (), [
' Query ' => [
' hero ' => function ( $ rootValue , $ arguments ) {
return getHero ( $ arguments [ ' episode ' ] ?? null );
},
],
]);
$ result = graphql ( $ schema , '
query HeroNameQuery {
hero {
name
}
} ' );
print_r ( $ result );上面的脚本产生以下输出:
Array
(
[data] => Array
(
[hero] => Array
(
[name] => " R2-D2 "
)
)
)此示例中使用的GraphQL架构文件包含以下内容:
schema {
query : Query
}
type Query {
hero ( episode : Episode ): Character
human ( id : String ! ): Human
droid ( id : String ! ): Droid
}
interface Character {
id : String !
name : String
friends : [ Character ]
appearsIn : [ Episode ]
}
type Human implements Character {
id : String !
name : String
friends : [ Character ]
appearsIn : [ Episode ]
homePlanet : String
}
type Droid implements Character {
id : String !
name : String
friends : [ Character ]
appearsIn : [ Episode ]
primaryFunction : String
}
enum Episode { NEWHOPE , EMPIRE , JEDI }创建模式
为了针对您的GraphQl API执行查询,您首先需要定义API的结构。这是通过创建模式来完成的。有两种方法可以这样做,您可以使用SDL进行操作,也可以通过编程方式进行操作。但是,我们强烈建议您使用SDL,因为它更容易使用。要从SDL制作可执行模式,您需要调用buildSchema函数。
buildSchema函数需要三个参数:
-
$source架构定义(SDL)作为Source实例 $resolverRegistry一个关联阵列或包含所有解析器的ResolverRegistry实例$options构建模式的选项,其中还包括自定义类型和指令
要创建Source实例,您可以使用提供的FileSourceBuilder或MultiFileSourceBuilder类。
解析器注册表
解析器注册表本质上是一张平面地图,其类型名称为键及其相应的解析器实例作为其值。对于较小的项目,您可以使用关联数组和Lambda功能来定义您的解析器注册表。但是,在较大的项目中,我们建议您实施自己的解析器。您可以在“解析器”部分下阅读有关解析器的更多信息。
关联阵列示例:
$ schema = buildSchema ( $ source , [
' Query ' => [
' hero ' => function ( $ rootValue , $ arguments ) {
return getHero ( $ arguments [ ' episode ' ] ?? null );
},
],
]);解析器类示例:
$ schema = buildSchema ( $ source , [
' Query ' => [
' hero ' => new HeroResolver (),
],
]);解析器中间件
如果您发现自己在多个解析器中编写相同的逻辑,则应考虑使用中间件。解析器中间件允许您有效地管理多个解析器的功能。
中间件示例之前:
$ resolverRegistry = new ResolverRegristry ([
' Query ' => [
' hero ' => function ( $ rootValue , $ arguments ) {
return getHero ( $ arguments [ ' episode ' ] ?? null );
},
],
], [
' middleware ' => [ new BeforeMiddleware ()],
]);
$ schema = buildSchema ( $ source , $ resolverRegistry ); class BeforeMiddleware implements ResolverMiddlewareInterface
{
public function resolve ( callable $ resolveCallback , $ rootValue , array $ arguments , $ context , ResolveInfo $ info ) {
$ newRootValue = $ this -> doSomethingBefore ();
return $ resolveCallback ( $ newRootValue , $ arguments , $ context , $ info );
}
}中间件之后示例:
$ resolverRegistry = new ResolverRegristry ([
' Query ' => [
' hero ' => function ( $ rootValue , $ arguments ) {
return getHero ( $ arguments [ ' episode ' ] ?? null );
},
],
], [
' middleware ' => [ new AfterMiddleware ()],
]);
$ schema = buildSchema ( $ source , $ resolverRegistry ); class AfterMiddleware implements ResolverMiddlewareInterface
{
public function resolve ( callable $ resolveCallback , $ rootValue , array $ arguments , $ context , ResolveInfo $ info ) {
$ result = $ resolveCallback ( $ rootValue , $ arguments , $ context , $ info );
$ this -> doSomethingAfter ();
return $ result ;
}
}解析器中间件对于许多事情都有用。例如记录,输入消毒,性能测量,授权和缓存。
如果您想了解有关模式的更多信息,则可以参考规范。
执行
查询
要执行针对您的架构的查询,您需要调用graphql函数并将其传递您的模式以及您希望执行的查询。您还可以通过更改查询来运行突变和订阅。
$ query = '
query HeroNameQuery {
hero {
name
}
} ' ;
$ result = graphql ( $ schema , $ query );如果您想了解有关查询的更多信息,则可以参考规范。
解析器
模式中的每种类型都有与之关联的解析器,可以解决实际值。但是,大多数类型不需要自定义解析器,因为它们可以使用默认的解析器解决。通常,这些解析器是lambda功能,但是您也可以通过扩展AbstractTypeResolver或AbstractFieldResolver来定义自己的解析器。另外,您还可以直接实现ResolverInterface 。
解析器功能会收到四个参数:
-
$rootValue父对象,在某些情况下也可以null -
$arguments查询中提供给字段的参数 $context一个传递给每个可以保存重要上下文信息的解析器的值$info一个值,该值包含与当前查询相关的特定领域信息
lambda功能示例:
function ( $ rootValue , array $ arguments , $ context , ResolveInfo $ info ): string {
return [
' type ' => ' Human ' ,
' id ' => ' 1000 ' ,
' name ' => ' Luke Skywalker ' ,
' friends ' => [ ' 1002 ' , ' 1003 ' , ' 2000 ' , ' 2001 ' ],
' appearsIn ' => [ ' NEWHOPE ' , ' EMPIRE ' , ' JEDI ' ],
' homePlanet ' => ' Tatooine ' ,
];
}类型解析器示例:
class HumanResolver extends AbstractTypeResolver
{
public function resolveName ( $ rootValue , array $ arguments , $ context , ResolveInfo $ info ): string
{
return $ rootValue [ ' name ' ];
}
}现场解析器示例:
class NameResolver extends AbstractFieldResolver
{
public function resolve ( $ rootValue , array $ arguments , $ context , ResolveInfo $ info ): string
{
return $ rootValue [ ' name ' ];
}
}N+1问题
解析器功能可以返回值,承诺或一系列承诺。下面的该解析器功能说明了如何使用承诺解决N+1问题,可以在此测试案例中找到完整的示例。
$ movieType = newObjectType ([
' fields ' => [
' title ' => [ ' type ' => stringType ()],
' director ' => [
' type ' => $ directorType ,
' resolve ' => function ( $ movie , $ args ) {
DirectorBuffer:: add ( $ movie [ ' directorId ' ]);
return new Promise ( function ( callable $ resolve , callable $ reject ) use ( $ movie ) {
DirectorBuffer:: loadBuffered ();
$ resolve (DirectorBuffer:: get ( $ movie [ ' directorId ' ]));
});
}
]
]
]);变量
通过将查询传递到graphql函数时,您可以通过变量传递。
$ query = '
query HeroNameQuery($id: ID!) {
hero(id: $id) {
name
}
} ' ;
$ variables = [ ' id ' => ' 1000 ' ];
$ result = graphql ( $ schema , $ query , null , null , $ variables );语境
如果您需要将一些重要的上下文信息传递给查询,则可以使用graphql上的$contextValues参数进行此操作。这些数据将作为$context参数传递给所有解析器。
$ contextValues = [
' currentlyLoggedInUser ' => $ currentlyLoggedInUser ,
];
$ result = graphql ( $ schema , $ query , null , $ contextValues , $ variables );标量
架构中的叶节点称为标量,每个标量都分解为某些具体数据。 GraphQL中的内置或指定标量如下:
- 布尔
- 漂浮
- int
- ID
- 细绳
自定义标量
除指定的标量外,您还可以定义自己的自定义标量,并通过将它们传递到buildSchema函数作为$options参数的一部分,让您的模式知道它们。
自定义日期标量类型示例:
$ dateType = newScalarType ([
' name ' => ' Date ' ,
' serialize ' => function ( $ value ) {
if ( $ value instanceof DateTime) {
return $ value -> format ( ' Y-m-d ' );
}
return null ;
},
' parseValue ' => function ( $ value ) {
if ( is_string ( $ value )){
return new DateTime ( $ value );
}
return null ;
},
' parseLiteral ' => function ( $ node ) {
if ( $ node instanceof StringValueNode) {
return new DateTime ( $ node -> getValue ());
}
return null ;
},
]);
$ schema = buildSchema ( $ source , [
' Query ' => QueryResolver::class,
[
' types ' => [ $ dateType ],
],
]);每个标量都必须强制强制,这是由三个不同功能完成的。 serialize函数将PHP值转换为相应的输出值。 parseValue函数将变量输入值转换为相应的PHP值,而parseLiteral函数将AST字面函数转换为相应的PHP值。
高级用法
如果您正在寻找此文档尚未涵盖的内容,那么最好的选择就是看该项目中的测试。您会惊讶于您在那里找到多少个例子。
一体化
拉拉维尔
这是一个示例,演示了如何在Laravel项目中使用此库。您需要一个应用程序服务来将此库曝光到您的应用程序,一个服务提供商可以注册该服务,控制器和处理GraphQl Post请求的路由。
app/graphql/graphqlservice.php
class GraphQLService
{
private $ schema ;
public function __construct ( Schema $ schema )
{
$ this -> schema = $ schema ;
}
public function executeQuery ( string $ query , array $ variables , ? string $ operationName ): array
{
return graphql ( $ this -> schema , $ query , null , null , $ variables , $ operationName );
}
}app/graphql/graphqlServiceProvider.php
class GraphQLServiceProvider
{
public function register ()
{
$ this -> app -> singleton (GraphQLService::class, function () {
$ schemaDef = file_get_contents ( __DIR__ . ' /schema.graphqls ' );
$ executableSchema = buildSchema ( $ schemaDef , [
' Query ' => QueryResolver::class,
]);
return new GraphQLService ( $ executableSchema );
});
}
}app/graphql/graphqlcontroller.php
class GraphQLController extends Controller
{
private $ graphqlService ;
public function __construct ( GraphQLService $ graphqlService )
{
$ this -> graphqlService = $ graphqlService ;
}
public function handle ( Request $ request ): JsonResponse
{
$ query = $ request -> get ( ' query ' );
$ variables = $ request -> get ( ' variables ' ) ?? [];
$ operationName = $ request -> get ( ' operationName ' );
$ result = $ this -> graphqlService -> executeQuery ( $ query , $ variables , $ operationName );
return response ()-> json ( $ result );
}
}路由/api.php
Route:: post ( ' /graphql ' , ' appGraphQLGraphQLController@handle ' );贡献者
由于所有贡献的人,该项目的存在。贡献。
支持者
感谢我们所有的支持者!成为支持者
赞助商
通过成为赞助商来支持这个项目。您的徽标将在此处显示您网站的链接。成为赞助商
执照
请参阅许可证。
通过命令行克隆项目: