fandogh / graphql
Facebook GraphQL for Laravel
Requires
- php: >=5.5.9
- illuminate/support: 5.*
- webonyx/graphql-php: ~0.5
Requires (Dev)
- orchestra/testbench: ~3.1
README
使用 Facebook GraphQL 与 Laravel 5。它基于 PHP 实现 这里。您可以在 GraphQL 简介 中了解 GraphQL 的更多信息(在 React 博客上),或者您还可以阅读 GraphQL 规范。这是一个正在进行中的项目。
此包与 Eloquent 模型(或任何其他数据源)兼容。请参阅下面的示例。
安装
依赖
安装
1- 在您的 composer.json
中通过 Composer 引入包。
{ "require": { "folklore/graphql": "0.4.*" } }
2- 运行 Composer 以安装或更新新要求。
$ composer install
或
$ composer update
3- 将服务提供者添加到您的 app/config/app.php
文件。
'Folklore\GraphQL\GraphQLServiceProvider',
4- 将外观添加到您的 app/config/app.php
文件。
'GraphQL' => 'Folklore\GraphQL\Support\Facades\GraphQL',
5- 发布配置文件
$ php artisan vendor:publish --provider="Folklore\GraphQL\GraphQLServiceProvider"
6- 查看配置文件
config/graphql.php
使用方法
高级用法
创建查询
首先您需要创建一个类型。
namespace App\GraphQL\Type; use GraphQL\Type\Definition\Type; use Folklore\GraphQL\Support\Type as GraphQLType; class UserType extends GraphQLType { protected $attributes = [ 'name' => 'User', 'description' => 'A user' ]; public function fields() { return [ 'id' => [ 'type' => Type::nonNull(Type::string()), 'description' => 'The id of the user' ], 'email' => [ 'type' => Type::string(), 'description' => 'The email of user' ] ]; } // If you want to resolve the field yourself, you can declare a method // with the following format resolve[FIELD_NAME]Field() protected function resolveEmailField($root, $args) { return strtolower($root->email); } }
将类型添加到 config/graphql.php
配置文件
'types' => [ 'user' => 'App\GraphQL\Type\UserType' ]
您还可以使用 GraphQL
外观将类型添加到服务提供者中。
GraphQL::addType('App\GraphQL\Type\UserType', 'user');
然后您需要定义一个返回此类型(或列表)的查询。您还可以指定在 resolve 方法中使用的参数。
namespace App\GraphQL\Query; use GraphQL; use GraphQL\Type\Definition\Type; use Folklore\GraphQL\Support\Query; use App\User; class UsersQuery extends Query { protected $attributes = [ 'name' => 'Users query' ]; public function type() { return Type::listOf(GraphQL::type('user')); } public function args() { return [ 'id' => ['name' => 'id', 'type' => Type::string()], 'email' => ['name' => 'email', 'type' => Type::string()] ]; } public function resolve($root, $args) { if(isset($args['id'])) { return User::where('id' , $args['id'])->get(); } else if(isset($args['email'])) { return User::where('email', $args['email'])->get(); } else { return User::all(); } } }
将查询添加到 config/graphql.php
配置文件
'schema' => [ 'query' => [ 'users' => 'App\GraphQL\Query\UsersQuery' ], // ... ]
或使用 GraphQL
外观
GraphQL::addQuery('App\GraphQL\Query\UsersQuery', 'users');
这样就完成了。您应该能够通过向 /graphql
(或您在配置中选择的任何内容)的 URL 发送请求来查询 GraphQL。尝试使用以下 query
输入进行 GET 请求
query FetchUsers {
users {
id
email
}
}
例如,如果您使用 homestead
http://homestead.app/graphql?query=query+FetchUsers{users{id,email}}
创建突变
突变类似于任何其他查询,它接受参数(这些参数将用于执行突变)并返回特定类型的对象。
例如,更新用户密码的突变。首先您需要定义突变。
namespace App\GraphQL\Mutation; use GraphQL; use GraphQL\Type\Definition\Type; use Folklore\GraphQL\Support\Mutation; use App\User; class UpdateUserPasswordMutation extends Mutation { protected $attributes = [ 'name' => 'UpdateUserPassword' ]; public function type() { return GraphQL::type('user'); } public function args() { return [ 'id' => ['name' => 'id', 'type' => Type::nonNull(Type::string())], 'password' => ['name' => 'password', 'type' => Type::nonNull(Type::string())] ]; } public function resolve($root, $args) { $user = User::find($args['id']); if(!$user) { return null; } $user->password = bcrypt($args['password']); $user->save(); return $user; } }
如您在 resolve
方法中看到的,您使用参数来更新您的模型并返回它。
然后您将突变添加到 config/graphql.php
配置文件
'schema' => [ 'mutation' => [ 'updateUserPassword' => 'App\GraphQL\Mutation\UpdateUserPasswordMutation' ], // ... ]
或使用 GraphQL
外观
GraphQL::addMutation('App\GraphQL\Mutation\UpdateUserPasswordMutation', 'updateUserPassword');
然后您应该能够在您的端点上使用以下查询来执行突变。
mutation {
updateUserPassword(id: "1", password: "newpassword") {
id
email
}
}
在突变中添加验证
可以为突变添加验证规则。它使用 Laravel 的 Validator
对 args
执行验证。
在创建突变时,您可以通过以下方式添加一个方法来定义应用的验证规则
namespace App\GraphQL\Mutation; use GraphQL; use GraphQL\Type\Definition\Type; use Folklore\GraphQL\Support\Mutation; use App\User; class UpdateUserEmailMutation extends Mutation { protected $attributes = [ 'name' => 'UpdateUserEmail' ]; public function type() { return GraphQL::type('user'); } public function args() { return [ 'id' => ['name' => 'id', 'type' => Type::string()], 'email' => ['name' => 'password', 'type' => Type::string()] ]; } public function rules() { return [ 'id' => ['required'], 'email' => ['required', 'email'] ]; } public function resolve($root, $args) { $user = User::find($args['id']); if(!$user) { return null; } $user->email = $args['email']; $user->save(); return $user; } }
或者您可以通过每个参数定义规则
class UpdateUserEmailMutation extends Mutation { //... public function args() { return [ 'id' => [ 'name' => 'id', 'type' => Type::string(), 'rules' => ['required'] ], 'email' => [ 'name' => 'password', 'type' => Type::string(), 'rules' => ['required', 'email'] ] ]; } //... }
在执行突变时,它将返回验证错误。由于 GraphQL 规范定义了错误的一定格式,验证错误消息被添加到错误对象中作为额外的 validation
属性。要查找验证错误,您应该检查一个 message
等于 'validation'
的错误,然后 validation
属性将包含由 Laravel Validator 返回的正常错误消息。
{ "data": { "updateUserEmail": null }, "errors": [ { "message": "validation", "locations": [ { "line": 1, "column": 20 } ], "validation": { "email": [ "The email is invalid." ] } } ] }
高级用法
查询变量
GraphQL 允许你在查询中使用变量,这样你就不需要“硬编码”值。操作方式如下
query FetchUserByID($id: String) {
user(id: $id) {
id
email
}
}
当你查询 GraphQL 端点时,你可以传递一个 params
参数。
http://homestead.app/graphql?query=query+FetchUserByID($id:String){user(id:$id){id,email}}¶ms={"id":"1"}
自定义字段
如果你想在多个类型中重复使用一个字段,你也可以将其定义为一个类。
namespace App\GraphQL\Fields; use GraphQL\Type\Definition\Type; use Folklore\GraphQL\Support\Field; class PictureField extends Field { protected $attributes = [ 'description' => 'A picture' ]; public function args() { return [ 'width' => [ 'type' => Type::int(), 'description' => 'The width of the picture' ], 'height' => [ 'type' => Type::int(), 'description' => 'The height of the picture' ] ]; } protected function resolve($root, $args) { $width = isset($args['width']) ? $args['width']:100; $height = isset($args['height']) ? $args['height']:100; return 'http://placehold.it/'.$width.'x'.$height; } }
然后你可以在你的类型声明中使用它
namespace App\GraphQL\Type; use GraphQL\Type\Definition\Type; use Folklore\GraphQL\Support\Type as GraphQLType; class UserType extends GraphQLType { protected $attributes = [ 'name' => 'User', 'description' => 'A user' ]; public function fields() { return [ 'id' => [ 'type' => Type::nonNull(Type::string()), 'description' => 'The id of the user' ], 'email' => [ 'type' => Type::string(), 'description' => 'The email of user' ], //Instead of passing an array, you pass a class path to your custom field 'picture' => App\GraphQL\Fields\PictureField::class ]; } }
预加载关系
查询的 resolve 方法传入的第三个参数是 GraphQL\Type\Definition\ResolveInfo
的一个实例,你可以用它来检索请求中的键。以下是一个使用此信息来预加载相关 Eloquent 模型的示例。
你的查询可能看起来像这样
namespace App\GraphQL\Query; use GraphQL; use GraphQL\Type\Definition\Type; use GraphQL\Type\Definition\ResolveInfo; use Folklore\GraphQL\Support\Query; use App\User; class UsersQuery extends Query { protected $attributes = [ 'name' => 'Users query' ]; public function type() { return Type::listOf(GraphQL::type('user')); } public function args() { return [ 'id' => ['name' => 'id', 'type' => Type::string()], 'email' => ['name' => 'email', 'type' => Type::string()] ]; } public function resolve($root, $args, ResolveInfo $info) { $fields = $info->getFieldSelection($depth = 3); $users = User::query(); foreach ($fields as $field => $keys) { if ($field === 'profile') { $users->with('profile'); } if ($field === 'posts') { $users->with('posts'); } } return $users->get(); } }
你的用户类型可能看起来像这样
<?php namespace App\GraphQL\Type; use Folklore\GraphQL\Support\Facades\GraphQL; use GraphQL\Type\Definition\Type; use Folklore\GraphQL\Support\Type as GraphQLType; class UserType extends GraphQLType { /** * @var array */ protected $attributes = [ 'name' => 'User', 'description' => 'A user', ]; /** * @return array */ public function fields() { return [ 'uuid' => [ 'type' => Type::nonNull(Type::string()), 'description' => 'The uuid of the user' ], 'email' => [ 'type' => Type::nonNull(Type::string()), 'description' => 'The email of user' ], 'profile' => [ 'type' => GraphQL::type('Profile'), 'description' => 'The user profile', ], 'posts' => [ 'type' => Type::listOf(GraphQL::type('Post')), 'description' => 'The user posts', ] ]; } }
到目前为止,我们有了预期的配置文件和帖子类型
class ProfileType extends GraphQLType { protected $attributes = [ 'name' => 'Profile', 'description' => 'A user profile', ]; public function fields() { return [ 'name' => [ 'type' => Type::string(), 'description' => 'The name of user' ] ]; } }
class PostType extends GraphQLType { protected $attributes = [ 'name' => 'Post', 'description' => 'A post', ]; public function fields() { return [ 'title' => [ 'type' => Type::nonNull(Type::string()), 'description' => 'The title of the post' ], 'body' => [ 'type' => Type::string(), 'description' => 'The body the post' ] ]; } }
最后,如果你的查询使用的是 Homestead,那么它可能看起来像这样
例如,如果您使用 homestead
http://homestead.app/graphql?query=query+FetchUsers{users{uuid, email, team{name}}}