在 Laravel 中使用 GraphQL 一 [获取数据]

在 Laravel 中使用 GraphQL#

什么是 GraphQL?#

GraphQL 是一种 API 查询语言,GraphQL 对你的 API 中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让 API 更容易地随着时间推移而演进,还能用于构建强大的开发者工具。

简单来说,GraphQL 不同于 REST API,REST API 请求多个资源时得载入多个 URL,而 GraphQL 可以通过一次请求就获取你应用所需的所有数据。这样一来,即使是比较慢的移动网络连接下,使用 GraphQL 的应用也能表现得足够迅速。查询方式类似下面这样子:

{
    user {
        id
        name
        job {
            name
            description
        }
    }
}

// 查询得到的数据格式是:

{
  "data": {
    "users": [
      {
        "id": 1,
        "name": "kwen",
        "job": [
          {
            "name": "前端开发工程师",
            "description": "前端前端"
          }
        ]
      },
      {
        "id": 2,
        "name": "kwen1",
        "job": [
          {
            "name": "PHP开发工程师",
            "description": "PHP"
          }
        ]
      }
    ]
  }
}

你可以在 这里 查看更多关于 GraphQL 的信息

在 Laravel 中使用 GraphQL#

以下我会用一个简单的 demo 来演示如何使用

1、安装 Laravel#

$ composer global require "laravel/installer"
$ laravel new laravel-graphql-test
$ cd laravel-graphql-test

我这里使用的是 valet 作为开发环境,详细的安装也可以到文档中查看

2、安装 graphql-laravel package#

修改 composer.json#

$ composer require rebing/graphql-laravel

添加 service provider (laravel5.5 + 会自动注册)#

// 添加到app/config/app.php
Rebing\GraphQL\GraphQLServiceProvider::class,
// 添加alias
'GraphQL' => 'Rebing\GraphQL\Support\Facades\GraphQL',

生成配置文件#

$ php artisan vendor:publish --provider="Rebing\GraphQL\GraphQLServiceProvider"

然后就可以到 config/graphql.php 查看配置信息了

3、创建数据模型#

生成模型和数据库表迁移文件#

$ php artisan make:model Job -m

Model created successfully.
Created Migration: 2018_02_14_152840_create_jobs_table

建立模型关系#

// app/Models/User.php
...
class User extends Authenticatable 
{
    ...
    public function job()
    {
        return $this->hasMany('App\Models\Job');
    }
}
// app/Models/Job.php
...
class Job extends Model
{
    public function user()
    {
        return $this->belongsTo("App\Models\User");
    }
}

修改 migration#

// xxx_create_jobs_table.php
...
class CreateJobsTable extends Migration
{
    public function up()
    {
        Schema::create('jobs', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('user_id');
            $table->string('name');
            $table->text('description')->nullable();
            $table->timestamps();
        });
    }
    ...
}

迁移 migration#

$ php artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated:  2014_10_12_000000_create_users_table
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated:  2014_10_12_100000_create_password_resets_table
Migrating: 2018_02_14_152840_create_jobs_table
Migrated:  2018_02_14_152840_create_jobs_table

4、创建 GraphQL 的 Query 和 Type#

GraphQL 是一个基于类型系统来执行查询的,所以需要定义好暴露的查询接口 (Query) 以及 接口的类型 (Type)

Type 会帮助我们格式化查询结果的类型,一般为 boolean、string、float、int 等,另外还可以定义自定义类型

目录结构#

GraphQL目录结构
图为 GraphQL 目录结构

定义 Type#

// app/GraphQL/Type/UsersType.php

<?php
namespace App\GraphQL\Type;

use App\Models\User;
use GraphQL;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Type as GraphQLType;

class UsersType extends GraphQLType
{
    protected $attributes = [
        'name' => 'Users',
        'description' => '用户',
        'model' => User::class
    ];

    /**
     * 定义返回的字段接口
     * @return array
     */
    public function fields()
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => '用户id'
            ],
            'name' => [
                'type' => Type::string(),
                'description' => '用户名'
            ],
            'email' => [
                'type' => Type::string(),
                'description' => '用户的email'
            ],
            'job' => [
                'type' => Type::listOf(GraphQL::type('jobs')),
                'description' => '用户的工作字段'
            ]
        ];
    }
}
// app/GraphQL/Type/JobsType.php

<?php
namespace App\GraphQL\Type;

use App\Models\Job;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Type as GraphQLType;

class JobsType extends GraphQLType
{
    protected $attributes = [
        'name' => 'jobs',
        'description' => '工作',
        'model' => Job::class
    ];


    public function fields()
    {
        return [
            'id' => [
                'type' => Type::nonNull(Type::int()),
                'description' => '工作id'
            ],
            'name' => [
                'type' => Type::string(),
                'description' => '工作名'
            ],
            'description' => [
                'type' => Type::string(),
                'description' => '工作职责描述'
            ]
        ];
    }
}

定义查询接口 Query#

// app/GraphQL/Query/UsersQuery.php

<?php

namespace App\GraphQL\Query;

use GraphQL;
use App\Models\User;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Query;

class UsersQuery extends Query
{
    protected $attributes = [
        'name' => 'users'
    ];

    public function type()
    {
        return Type::listOf(GraphQL::type('users'));
    }

    /**
     * 接收参数的类型定义
     * @return array
     */
    public function args()
    {
        return [
            'id' => ['name' => 'id', 'type' => Type::int()],
            'email' => ['name' => 'email', 'type' => Type::string()],
            'limit' => ['name' => 'limit', 'type' => Type::int()],
        ];
    }

    /**
     * @param $root
     * @param $args 传入参数
     *
     * 处理请求的逻辑
     * @return mixed
     */
    public function resolve($root, $args)
    {
        $user = new User;

        if(isset($args['limit']) ) {
            $user =  $user->limit($args['limit']);
        }

        if(isset($args['id']))
        {
            $user = $user->where('id' , $args['id']);
        }

        if(isset($args['email']))
        {
            $user = $user->where('email', $args['email']);
        }

        return $user->get();
    }
}
// app/GraphQL/Query/JobsQuery.php

<?php

namespace App\GraphQL\Query;

use GraphQL;
use GraphQL\Type\Definition\Type;
use Folklore\GraphQL\Support\Query;

class JobsQuery extends Query
{
    protected $attributes = [
        'name' => 'jobs'
    ];

    public function type()
    {
        return Type::listOf(GraphQL::type('jobs'));
    }

    public function args()
    {
        return [
            'id' => ['name' => 'id', 'type' => Type::int()],
            'name' => ['name' => 'name', 'type' => Type::string()],
        ];
    }
}

5、测试结果#

接下来就可以开始测试了

填充测试数据#

因为数据库里面什么数据都没有,所以首先需要填充测试数据,这里使用的是 seed 进行填充

// database/seeds/UsersTableSeeder.php

...
class UsersTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('users')->insert([
            'name' => 'kwen',
            'email' => 'email@email.com',
            'password' => bcrypt('123456'),
        ]);
        DB::table('users')->insert([
            'name' => 'kwen1',
            'email' => 'email1@email.com',
            'password' => bcrypt('123456'),
        ]);
    }
}
// database/seeds/JobsTableSeeder.php

...
class JobsTableSeeder extends Seeder
{
    public function run()
    {
        DB::table('jobs')->insert([
            'user_id' => 1,
            'name' => '前端开发工程师',
            'description' => '前端前端'
        ]);
        DB::table('jobs')->insert([
            'user_id' => 2,
            'name' => 'PHP开发工程师',
            'description' => 'PHP'
        ]);
    }
}
// database/seeds/DatabaseSeeder.php

...
class DatabaseSeeder extends Seeder
{
    public function run()
    {
         $this->call(UsersTableSeeder::class);
         $this->call(JobsTableSeeder::class);
    }
}

修改完这两个文件之后使用 artisan 命令进行填充

$ php artisan db:seed
Seeding: UsersTableSeeder
Seeding: JobsTableSeeder

安装测试工具#

这里使用的是 GraphQL 可视化调试工具,专门针对 Laravel 的 noh4ck/laravel-graphiql

1、安装 laravel-graphiql

$ composer require "noh4ck/graphiql:@dev"

2、打开 config/app.php 并添加以下代码到 providers

Graphiql\GraphiqlServiceProvider::class

3、发布这个包并生成 config/graphiql.php 配置文件

$ php artisan graphiql:publish

测试数据#

运行 php artisan serve 然后打开 http://127.0.0.1:8000/graphql-ui 就可以打开测试工具的界面了

image

如果要查询某个特定 id 或者特定参数的 user ,则可以带参数进行查询

image

限定 2 个用户

image

所传参数的设置可以在 app/GraphQL/Query/UsersType.php 中设置,resolve 方法里面就是设置参数逻辑的

总结#

这篇文章简单地介绍了如何在 Laravel 中使用 GraphQL 作为 API 查询语言了,使用的是 noh4ck/laravel-graphiql ,但是使用中觉得这个包的还不够完美,如果在生成 Type 或者 Query 的时候能用 artisan 命令就好了,我已经在 github 上面提了 issue 并得到相关回复,应该很快就可以使用这些功能了。

有人说传统 Restful API 已死,GraphQL 永生,GraphQL 解决的就是 Restful 的缺点,但同时 GraphQL 也存在很多性能的问题,GraphQL 真正要完全替代 Restful API 还有很长一段路要走,让我们拭目以待吧!

往后文章中还会继续介绍这个包的更多用法,例如如何修改数据、增加数据、删除数据还有授权认证 authenticated 等等,第一次写文章,希望能多多支持。

本文 demo 可以到 github 上查看

下一节: 在 Laravel 中使用 GraphQL 二【修改数据】

参考#

博客

GraphQL 官网

本作品采用《CC 协议》,转载必须注明作者和本文链接
kwen
本帖由 Summer 于 7年前 加精
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
讨论数量: 19

我想请问一下,如何使用 php 获取 graphQL 接口的数据?php 是 client

7年前 评论

@Tinpont 你可以直接请求 graphql 接口,因为一般 graphql 只有一个接口就是 /graphql 除非你自定义其它,例如请求
http://localhost:8000/graphql?query=query+FetchUsers{users{id,email}}
在我的 demo 中就可以获取到

{
  data: {
    users: [
      id: 1,
      email: 'email@email.com'
    ]
  }
}
7年前 评论

file
file
有没有遇到这个问题

6年前 评论

@Benny 这里应该需要 alias 一下

echo alias laravel='~/.composer/vendor/bin/laravel' >> ~/.zshrc

source ~/.zshrc
6年前 评论

@科大大 非常感谢,采纳 成功了

6年前 评论

file修改了 host 文件 ,就不能访问本地环境了

6年前 评论

@Benny nslookup 看一下是解析到哪了

6年前 评论

@Wi1dcard dnsmasq 出了问题, 执行 valet install 解决了

6年前 评论

@张闯 Json 看起来是没有找到 users 的 type,config/graphql 里面定义了 users 这个 type 了么,如果定义了还没用的话运行 php artisan config:clear 试试

6年前 评论

@张闯 Json 那应该是依赖的问题,我记得很早之前那个包是叫 laravel-graphql 的,可能我在写第二篇文章的时候更新了

6年前 评论
不温柔

图片已经失效了,能重新上传一下吗

6年前 评论

现在好像又有一个 https://lighthouse-php.com/

5年前 评论

@likunyan 是的,扩展包视频里面也有这个包的视频,可以看看

5年前 评论

file

file
不知道为什么,我的这个 type () 方法提示非静态方法,一直不能调用

5年前 评论

@FeiYu-Lin 有可能是包的版本问题,确认下版本是否和我的 demo 一致

5年前 评论

noh4ck/graphiql 不支持 laravel7

4年前 评论

文中的 Folklore 应该改为 Rebing

4年前 评论