使用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
广告
将在 10 秒后关闭
bannerAds