使用Laravel和GraphQL进行API开发
注意:追加说明
在这篇文章中所采用的库已于2019年2月10日进行了归档???
对于以后考虑引入库的人来说,我建议使用nuwave/lighthouse。
请以中国本地语言进行再次表达,只需要一种选择:
=========================================================
尽管我们决定在公司引入GraphQL,但由于缺乏知识,我们在进行调查的同时逐步推进。
GraphQL的信息本来就很少,而且Laravel的信息也很少,所以我一直盯着源代码。
我想把自己整理的信息写成一篇文章,也兼顾整理自己的知识。
如果有错误,请指出来。
这是我第一次接触GraphQL,所以我在关键时刻感到非常激动。
由于整个内容变得太长了,我原本打算把它全部写出来。但是现在我只会描述Type(GraphQL独有的类型定义)、Query和Mutation。
系列文章
-
- LaravelとGraphQLでAPI開発
-
- Laravel 5.7 + GraphQL(Install編)
-
- Laravel 5.7 + GraphQL(ScalarType編)
-
- Laravel 5.7 + GraphQL(Relation編)
-
- Laravel 5.7 + GraphQL(Dump Server編)
-
- Laravel 5.7 + GraphQL(Pagination編)
-
- Laravel 5.7 + GraphQL(ErrorHandling編)
- Laravel 5.7 + GraphQL(Test編)
章节目录
-
- サンプルリポジトリの紹介
-
- GraphQL導入メリット
-
- Laravel5.7インストール
-
- GraphQLインストール
Laravel5.7注意点
追加されるGraphQLコマンド
追加されるGraphQLルーティング
GraphQLブラウザ実行環境
テストデータ作成
GraphQL Type (ユーザー型定義)
GraphQL Query (ユーザー取得)
ユーザー、ユーザーの一覧の取得例
GraphQL Mutation (ユーザー登録)
ユーザー登録例
バリデーション例
所感
样本数据库
本文中所使用的源代码可以在以下存储库找到:
https://github.com/ucan-lab/practice-laravel-graphql
我想以备查参考的方式,也可以保留提交链接。
GraphQL的优点
我整理了我个人感受到的优点。
-
- エンドポイント(ルーティング定義)を気にしなくていい
-
- Laravelバリデーションが使える
これが超絶便利
dump-serverでデバッグ効率アップ
Laravel5.7から追加された新機能
GraphQLの実行結果を確認しつつ、dumpの実行結果を確認できる
dump-serverを起動していればdumpをコメントしなくて良い
resolveを使えば融通が利く
クライアント側は欲しいパラメータだけ指定すれば良い(リソースの節約になる)
安装Laravel5.7
Laravel 5.7 + GraphQL的安装步骤
我已经写了一篇不慎传闻的文章。
GraphQL浏览器执行环境
$ php artisan serve
$ php artisan dump-server
您可以在GraphQL执行环境中通过浏览器对查询和变更进行尝试。
由于可以查看输入预测和输入历史记录,因此非常易于使用。
快捷键 jié
-
- control + shift + p クエリの整形
-
- control + Enter クエリの実行
-
- control + space 自動補完
IMEの切り替えショートカットとバッティングした
创建测试数据
我会制作一个简单的种子。
$ php artisan make:seeder UsersTableSeeder
$ php artisan migrate:fresh --seed
GraphQL类型(用户类型定义)
-
- https://github.com/ucan-lab/practice-laravel-graphql/commit/6ab1ad983c58cc904c5c0b6f3c8437b7221f1c23
- https://github.com/ucan-lab/practice-laravel-graphql/commit/9e8baf6d31470b42a6e13dc7f8c82fcf21841f1b
查询用于读取数据,而变更用于写入数据。因此,在使用它们之前,需要先定义GraphQL独特的类型,比如创建一个用户类型。
$ php artisan make:graphql:type UserType
app/GraphQL/Type/UserType.php ファイルが作成されます。
config/graphql.php に型を登録します。
'types' => [
App\GraphQL\Type\UserType::class,
],
应用/GraphQL/类型/UserType.php
<?php
declare(strict_types = 1);
namespace App\GraphQL\Type;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Type as BaseType;
use GraphQL;
/**
* User型の定義
*/
class UserType extends BaseType
{
/**
* 型名の定義と概要
*
* @var array
*/
protected $attributes = [
'name' => 'UserType',
'description' => 'A type'
];
/**
* フィールドに持たせる型と挙動を定義
*
* @return array
*/
public function fields() : array
{
return [
'id' => [
'type' => Type::id(),
'description' => 'The id of the user'
],
'name' => [
'type' => Type::string(),
'description' => 'The name of user',
],
'email' => [
'type' => Type::string(),
'description' => 'The email of user',
],
];
}
}
在fields方法中指定允许的列。只要不写像密码之类的项目,就可以不让其外露。
指定每列的标量类型。
Type::id() => intとの区別がわからないけど、idっぽいやつ
Type::string()
Type::boolean()
Type::int()
Type::float()
Type::listOf() => 配列
Type::nonNull() => NOT NULL
我在这里想,日期类型怎么处理呢……(将在另一篇文章中详细讨论)。
GraphQL查询(获取用户)
https://github.com/ucan-lab/practice-laravel-graphql/commit/e1f21f724043c6217527470e9886a70220dd0acb
https://github.com/ucan-lab/practice-laravel-graphql/commit/7a9abd13fd5c0777dd5ef19ba34735a43a56012d
https://github.com/ucan-lab/practice-laravel-graphql/commit/d79f2fcfb7029763d408066dec6ac4d89e55bc61
https://github.com/ucan-lab/practice-laravel-graphql/commit/0d43798739c1cc8a571085c76e72567d15ce366e
为了读取数据,我们定义一个查询。
$ php artisan make:graphql:query UserQuery
$ php artisan make:graphql:query UsersQuery
-
- app/GraphQL/Query/UserQuery.php
- app/GraphQL/Query/UsersQuery.php
由于有时需要分别获取单个和列表,因此将创建两种不同的用户查询。
将查询注册到 config 中,就像在 config/graphql.php 文件中进行类型定义时一样。
'schemas' => [
'default' => [
'query' => [
App\GraphQL\Query\UserQuery::class,
App\GraphQL\Query\UsersQuery::class,
],
'mutation' => [
]
]
],
应用/GraphQL/查询/UserQuery.php
<?php
declare(strict_types = 1);
namespace App\GraphQL\Query;
use Folklore\GraphQL\Support\Query;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL;
use App\User;
/**
* Userクエリの定義
*/
class UserQuery extends Query
{
/**
* クエリ名の定義と概要
*
* @var array
*/
protected $attributes = [
'name' => 'user',
'description' => 'user query'
];
/**
* クエリが扱う型を定義
*
* @return ObjectType
*/
public function type() : ObjectType
{
return GraphQL::type('UserType');
}
/**
* クエリが取り得る引数を定義
*
* @return array
*/
public function args() : array
{
return [
'id' => [
'name' => 'id',
'type' => Type::id(),
],
];
}
/**
* クエリに対する実処理
*
* @param array $root
* @param array $args
* @return User
*/
public function resolve(array $root, array $args) : User
{
$query = User::query();
if (isset($args['id'])) {
$query->where('id', $args['id']);
}
return $query->first();
}
}
$attributes プロパティの name キーに設定した名前がクエリ名として扱われます。
type() クエリが返すGraphQLの型を指定します。
args() クエリが受け取れるパラメータを定義します。(検索条件等)
resolve() コントローラの代わりにここで色々やります。
$args にパラメータが入ってくるのでそれに応じて実装しよう
処理が複雑になるとここが肥大化したり重複コードが量産されるので設計大事です
在GraphQL查询浏览器运行环境中进行测试。
在左侧写下查询。
query {
user {
id
name
email
}
}
结果将返回到右侧。
{
"data": {
"user": {
"id": "1",
"name": "Maximus Bechtelar",
"email": "tshields@example.net"
}
}
}
-
- フィールドを区切るカンマは合ってもなくてもいいらしい。
{user{id name email}} 極端な話、クエリはこれで通る。(今回は見やすさ重視でいきます)
queryも省略できる
{クエリ名{フィールド名}} で簡単に取得できる?
这个…简直是神一样的存在!!!创建一个查询类,将其注册到配置中就可以完成!!服务器端非常轻松,并且客户端可以简单地获取数据!
query {
user(id: 10) {
id
name
email
}
}
{
"data": {
"user": {
"id": "10",
"name": "Elyssa Marvin",
"email": "bryana.hyatt@example.com"
}
}
}
如果你传入一个用户id(id为10),你就可以获取到id为10的用户。如果没有指定id,那就默认获取第一个数据。但是,如果根本没有指定id,我想要报错。
在那个时候,我心想着。
在表单请求中编写验证是否很麻烦呢?(我会在变异中进行详细解释)。
应用程序/GraphQL/查询/UsersQuery.php
当需要显示用户详细页面时,可以使用UserQuery,但当需要显示用户列表时,由于希望获取多个用户,所以也提供了获取多个用户的示例。
<?php
namespace App\GraphQL\Query;
use Folklore\GraphQL\Support\Query;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ListOfType;
use GraphQL;
use Illuminate\Database\Eloquent\Collection;
use App\User;
/**
* Usersクエリの定義
*/
class UsersQuery extends Query
{
/**
* クエリ名の定義と概要
*
* @var array
*/
protected $attributes = [
'name' => 'users',
'description' => 'users query'
];
/**
* クエリが扱う型を定義
*
* @return ObjectType
*/
public function type() : ListOfType
{
return Type::listOf(GraphQL::type('UserType'));
}
/**
* クエリが取り得る引数を定義
*
* @return array
*/
public function args() : array
{
return [
'id' => [
'name' => 'id',
'type' => Type::id(),
],
'ids' => [
'name' => 'ids',
'type' => Type::listOf(Type::id()),
],
'email' => [
'name' => 'email',
'type' => Type::string(),
]
];
}
/**
* クエリに対する実処理
*
* @param array $root
* @param array $args
* @return Collection
*/
public function resolve(array $root, array $args) : Collection
{
$query = User::query();
if (isset($args['id'])) {
$query->where('id', $args['id']);
}
if (isset($args['ids'])) {
$query->whereIn('id', $args['ids']);
}
if (isset($args['email'])) {
$query->where('email', $args['email']);
}
return $query->get();
}
}
type() で ListOfType を返す
resolve で Collection を返す
要点有两点。
在UsersQuery中,我们使参数能够接收数组的ids。
query {
users(ids: [10, 15, 20]) {
id
name
email
}
}
{
"data": {
"users": [
{
"id": "10",
"name": "Elyssa Marvin",
"email": "bryana.hyatt@example.com"
},
{
"id": "15",
"name": "Mr. Jeromy Pacocha IV",
"email": "fredy13@example.net"
},
{
"id": "20",
"name": "Prof. Janelle Raynor IV",
"email": "gprice@example.org"
}
]
}
}
我已经获取到用户列表了。
听起来可以做得很好。
在这个时候,我想到了一个问题。关于页面分页,我们应该怎么处理呢?(我将会考虑这个问题)
GraphQL变更(用户注册)
-
- https://github.com/ucan-lab/practice-laravel-graphql/commit/64ab86bda1a82f621bf4a2a965e0e7ab8cfd8448
- https://github.com/ucan-lab/practice-laravel-graphql/commit/88bd1fadf1480b33bca2c0df78afd07dcbbfdf0d
为了写入数据,定义一个Mutation。
$ php artisan make:graphql:mutation CreateUserMutation
- app/GraphQL/Mutation/CreateUserMutation.php
因为有时候我们需要分别获取单个和列表,所以我将创建两种不同的用户查询。
虽然担心命名规则,但是通过按照动作分割创建似乎更简单一些。
与 config/graphql.php 文件中的类型定义时一样,将突变注册到配置中。
'schemas' => [
'default' => [
'query' => [
App\GraphQL\Query\UserQuery::class,
App\GraphQL\Query\UsersQuery::class,
],
'mutation' => [
App\GraphQL\Mutation\CreateUserMutation::class,
]
]
],
应用程序/GraphQL/变异/CreateUserMutation.php
<?php
declare(strict_types = 1);
namespace App\GraphQL\Mutation;
use Folklore\GraphQL\Support\Mutation;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;
use GraphQL;
use Illuminate\Support\Facades\Hash;
use App\User;
/**
* ユーザーを登録するミューテーション
*/
class CreateUserMutation extends Mutation
{
/**
* ミューテーション名の定義と概要
*
* @var array
*/
protected $attributes = [
'name' => 'CreateUser',
'description' => 'CreateUser mutation'
];
/**
* ミューテーションが扱う型を定義
*
* @return ObjectType
*/
public function type() : ObjectType
{
return GraphQL::type('UserType');
}
/**
* ミューテーションが取り得る引数を定義
*
* @return array
*/
public function args() : array
{
return [
'name' => [
'name' => 'name',
'type' => Type::string(),
'rules' => ['required', 'string', 'max:255'],
],
'email' => [
'name' => 'email',
'type' => Type::string(),
'rules' => ['required', 'string', 'email', 'max:255', 'unique:users'],
],
'password' => [
'name' => 'password',
'type' => Type::string(),
'rules' => ['required', 'string', 'min:6'],
],
];
}
/**
* ミューテーションに対する実処理
*
* @param array $root
* @param array $args
* @return User
*/
public function resolve($root, $args, $context, ResolveInfo $info) : User
{
$user = User::create([
'name' => $args['name'],
'email' => $args['email'],
'password' => Hash::make($args['password']),
]);
return $user;
}
}
我编写了一个注册用户的突变。它可以非常简单地编写。
在规则中,Laravel的验证可以直接使用,这是非常棒的,不需要创建表单请求。
在到达resolve之前,它会为我自动检查验证,我觉得这很棒。
GraphQL突变(用户注册)在浏览器执行环境中进行测试。
mutation {
CreateUser (
name: "test"
email: "sample@example.com"
password: "secret"
) {
id
name
email
}
}
{
"data": {
"CreateUser": {
"id": "31",
"name": "test",
"email": "sample@example.com"
}
}
}
由于突变,可以返回UserType,因此可以获取注册用户的信息。
由于使用了查询所使用的UserType,非常方便。
在验证错误时
mutation {
CreateUser (
email: "sample@example.com"
password: "secret"
) {
id
name
email
}
}
删除必填字段”name”并执行。
{
"data": {
"CreateUser": null
},
"errors": [
{
"message": "validation",
"locations": [
{
"line": 2,
"column": 3
}
],
"validation": {
"name": [
"The name field is required."
],
"email": [
"The email has already been taken."
]
}
}
]
}
这样就会返回验证错误。
由于电子邮件已经被注册了,所以会出现错误!
感受
GraphQL真是太厉害了。
只是关于Laravel+GraphQL的资料很少,而且错误的信息也很多,让我遇到了很多问题。
我不确定我的方法是否是最佳实践,所以想听取专家的意见。
-
- 日付型(スカラー型)の追加
- リレーションデータの取得
因为这篇文章变得很长了,所以我想在另一篇文章中介绍这个内容。
-
- Lighthouse がいいらしい
https://github.com/nuwave/lighthouse
我写到这里,但有个问题,好像使用Lighthouse更好。
因为我一开始不太了解GraphQL,所以首先尝试了这个。
我认为使用Lighthouse可以大大减少代码量,并且可以输出模式,这是超级有利的。
这样一来开发效率应该会更高。
请提供参考资料
请提供相关信息
请提供参考信息
请提供参考资讯
请提供参考数据
-
- https://github.com/Folkloreatelier/laravel-graphql
-
- https://graphql.org/learn
-
- https://qiita.com/nunulk/items/5c506d8ef391f5e9971c
- https://qiita.com/acro5piano/items/b9e3acd4af5ec14d7bb1