在 Laravel 中使用 GraphQL 一 [獲取資料]

科大大發表於2018-02-15

什麼是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

相關文章