在 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 于 6年前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 19

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

6年前 评论

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

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

file
file
有没有遇到这个问题

5年前 评论

@Benny 这里应该需要 alias 一下

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

source ~/.zshrc
5年前 评论

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

5年前 评论

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

5年前 评论

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

5年前 评论

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

5年前 评论

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

5年前 评论

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

5年前 评论
不温柔

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

4年前 评论

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

4年前 评论

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

4年前 评论

file

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

4年前 评论

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

4年前 评论

noh4ck/graphiql 不支持laravel7

3年前 评论

文中的 Folklore 应该改为 Rebing

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!