GraphQl
这是基于JavaScript参考实现的GraphQL规范的PHP实现。
相关项目
- DateTime标量
- 继电器支持
要求
- PHP版本> = 7.1
- EXT-MBSTRING
目录
安装
运行以下命令通过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 \' , \' app\\GraphQL\\GraphQLController@handle \' );
贡献者
由于所有贡献的人,该项目的存在。贡献。
支持者
感谢我们所有的支持者!成为支持者
赞助商
通过成为赞助商来支持这个项目。您的徽标将在此处显示您网站的链接。成为赞助商
执照
请参阅许可证。
