การสร้างเซิร์ฟเวอร์ GraphQL ด้วย Laravel
เผยแพร่แล้ว: 2022-03-11ในกรณีที่คุณยังไม่คุ้นเคย GraphQL เป็นภาษาคิวรีที่ใช้ในการโต้ตอบกับ API ของคุณ ซึ่งให้ประโยชน์บางประการเมื่อเทียบกับสถาปัตยกรรมทางเลือก เช่น REST GraphQL มีประโยชน์อย่างยิ่งเมื่อใช้เป็นปลายทางสำหรับแอปพลิเคชันมือถือและหน้าเดียว GraphQL ช่วยให้คุณค้นหาข้อมูลที่ซ้อนกันและข้อมูลที่เกี่ยวข้องในคำขอได้อย่างง่ายดาย ช่วยให้นักพัฒนาได้รับข้อมูลที่ต้องการในการเดินทางไปเซิร์ฟเวอร์ครั้งเดียว
Laravel เป็นเฟรมเวิร์กเว็บ PHP ที่ได้รับความนิยม มีเครื่องมือในตัวจำนวนมากเพื่อให้แอปพลิเคชันทำงานได้อย่างรวดเร็ว แต่ยังช่วยให้นักพัฒนาสามารถสลับการใช้งานของตนเองสำหรับอินเทอร์เฟซในตัวของ Laravel เมื่อต้องการ
แม้ว่าชุมชนโดยรอบทั้ง GraphQL และ Laravel จะเติบโตขึ้นอย่างมากเนื่องจากเป็นโอเพ่นซอร์ส แต่เอกสารที่อธิบายวิธีการใช้เทคโนโลยีทั้งสองนี้ร่วมกันก็ยังค่อนข้างหายาก
ในบทช่วยสอนนี้ ฉันจะแสดงวิธีสร้างเซิร์ฟเวอร์ GraphQL ของคุณเองโดยใช้ Laravel
ภาพรวมโครงการ
ก่อนที่เราจะเริ่มต้น เราจะต้องทำความคุ้นเคยกับโครงการที่เรากำลังพยายามสร้าง ในการทำเช่นนั้น เราจะกำหนดทรัพยากรของเราและสร้างสคีมา GraphQL ซึ่งเราจะใช้เพื่อให้บริการ API ของเราในภายหลัง
ทรัพยากรโครงการ
แอปพลิเคชันของเราจะประกอบด้วยสองแหล่งข้อมูล: บทความ และ ผู้ใช้ ทรัพยากรเหล่านี้จะถูกกำหนดเป็นประเภทวัตถุในสคีมา GraphQL ของเรา:
type User { id: ID! name: String! email: String! articles: [Article!]! } type Article { id: ID! title: String! content: String! author: User! }
เมื่อดูที่สคีมา เราจะเห็นว่าเรามีความสัมพันธ์แบบหนึ่งต่อกลุ่มระหว่างสองอ็อบเจ็กต์ของเรา ผู้ใช้สามารถเขียนบทความได้หลายบทความ และบทความหนึ่งมีผู้เขียน (ผู้ใช้) ที่ได้รับมอบหมาย
ตอนนี้เราได้กำหนดประเภทวัตถุแล้ว เราจึงต้องการวิธีสร้างและสืบค้นข้อมูลของเรา ดังนั้นมากำหนดวัตถุการสืบค้นและการกลายพันธุ์ของเรา:
type Query { user(id: ID!): User users: [User!]! article(id: ID!): Article articles: [Article!]! } type Mutation { createUser(name: String!, email: String!, password: String!): User createArticle(title: String!, content: String!): Article }
การตั้งค่าโครงการ Laravel ของเรา
ตอนนี้เราได้กำหนดสคีมา GraphQL แล้ว มาเริ่มโปรเจ็กต์ Laravel กัน เริ่มต้นด้วยการสร้าง Laravel ใหม่ผ่านโปรเจ็กต์ Composer:
$ composer create-project --prefer-dist laravel/laravel laravel-graphql
เพื่อให้แน่ใจว่าทุกอย่างทำงานได้ ให้เปิดเครื่องเซิร์ฟเวอร์ของเราและตรวจดูให้แน่ใจว่าเราเห็นหน้าเริ่มต้นของ Laravel:
$ cd laravel-graphql $ php artisan serve Laravel development server started: <http://127.0.0.1:8000>
โมเดลฐานข้อมูลและการย้ายข้อมูล
สำหรับวัตถุประสงค์ของบทความนี้ เราจะใช้ SQLite เรามาทำการเปลี่ยนแปลงต่อไปนี้กับไฟล์ .env
เริ่มต้นกัน:
DB_CONNECTION=sqlite # DB_HOST= # DB_PORT= # DB_DATABASE=database.sqlite # DB_USERNAME= # DB_PASSWORD=
ต่อไป มาสร้างไฟล์ฐานข้อมูลของเรากัน:
$ touch ./database/database.sqlite
Laravel มาพร้อมกับโมเดลผู้ใช้และไฟล์การย้ายข้อมูลพื้นฐานบางส่วน มาเพิ่มคอลัมน์ api_token
อย่างรวดเร็วในไฟล์การโยกย้าย CreateUsersTable
ที่ Laravel มอบให้เรา:
/database/migrations/XXXX_XX_XX_000000_create_users_table.php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateUsersTable extends Migration { /** * Run the migrations. */ public function up() { Schema::create('users', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->string('api_token', 80)->unique()->nullable()->default(null); $table->rememberToken(); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down() { Schema::dropIfExists('users'); } }
เราจะวนกลับไปที่คอลัมน์เพิ่มเติมนี้ในภายหลังในบทความเมื่อเราได้รับการอนุญาต ตอนนี้ ไปข้างหน้าและสร้างแบบจำลองบทความของเราและไฟล์การโยกย้ายเพื่อสร้างตารางที่เกี่ยวข้อง:
$ php artisan make:model Article -m
หมายเหตุ: ตัวเลือก -m จะสร้างไฟล์การโยกย้ายสำหรับโมเดลบทความที่สร้างขึ้นใหม่ของเรา
มาทำการปรับเปลี่ยนบางอย่างกับไฟล์การโยกย้ายที่สร้างขึ้น:
use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateArticlesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('articles', function (Blueprint $table) { $table->bigIncrements('id'); $table->unsignedBigInteger('user_id'); $table->string('title'); $table->text('content'); $table->timestamps(); $table->foreign('user_id')->references('id')->on('users'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('articles'); } }
เราได้เพิ่มคีย์นอกที่ชี้ไปที่ id
ในตาราง users
ของเรา เช่นเดียวกับคอลัมน์ title
และ content
ที่เรากำหนดไว้ในสคีมา GraphQL
ตอนนี้เราได้กำหนดไฟล์การโยกย้ายแล้ว ให้ดำเนินการกับฐานข้อมูลของเรา:
$ php artisan migrate
ต่อไป มาอัปเดตโมเดลของเราโดยกำหนดความสัมพันธ์ที่จำเป็น:
app/User.php namespace App; use Illuminate\Notifications\Notifiable; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; // ... /** * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function articles() { return $this->hasMany(Article::class); } }
app/Article.php namespace App; use Illuminate\Database\Eloquent\Model; class Article extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'title', 'content', ]; /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function user() { return $this->belongsTo(User::class); } }
การเพาะฐานข้อมูล
ตอนนี้เราได้ตั้งค่าแบบจำลองและการย้ายข้อมูลแล้ว มาเริ่มต้นฐานข้อมูลของเรากัน เราจะเริ่มต้นด้วยการสร้างคลาส seeder สำหรับ articles
และตาราง users
ของเรา:
$ php artisan make:seeder UsersTableSeeder $ php artisan make:seeder ArticlesTableSeeder
ต่อไป มาตั้งค่าให้แทรกข้อมูลจำลองลงในฐานข้อมูล SQLite ของเรา:
database/seeds/UsersTableSeeder.php use App\User; use Illuminate\Database\Seeder; class UsersTableSeeder extends Seeder { /** * Run the database seeds. */ public function run() { \App\User::truncate(); $faker = \Faker\Factory::create(); $password = bcrypt('secret'); \App\User::create([ 'name' => $faker->name, 'email' => '[email protected]', 'password' => $password, ]); for ($i = 0; $i < 10; ++$i) { \App\User::create([ 'name' => $faker->name, 'email' => $faker->email, 'password' => $password, ]); } } }
database/seeds/ArticlesTableSeeder.php use App\Article; use Illuminate\Database\Seeder; class ArticlesTableSeeder extends Seeder { /** * Run the database seeds. */ public function run() { \App\Article::truncate(); \App\Article::unguard(); $faker = \Faker\Factory::create(); \App\User::all()->each(function ($user) use ($faker) { foreach (range(1, 5) as $i) { \App\Article::create([ 'user_id' => $user->id, 'title' => $faker->sentence, 'content' => $faker->paragraphs(3, true), ]); } }); } }
/database/seeds/DatabaseSeeder.php use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { $this->call(UsersTableSeeder::class); $this->call(ArticlesTableSeeder::class); } }
สุดท้าย ให้ดำเนินการต่อไปและเรียกใช้ฐานข้อมูลของเราเพื่อนำข้อมูลบางส่วนเข้าสู่ฐานข้อมูลของเรา:
$ php artisan db:seed
Laravel Lighthouse และเซิร์ฟเวอร์ GraphQL
ตอนนี้เราได้สร้างฐานข้อมูลและโมเดลแล้ว ถึงเวลาที่จะเริ่มสร้างเซิร์ฟเวอร์ GraphQL ของเรา ปัจจุบัน มีวิธีแก้ปัญหาหลายอย่างสำหรับ Laravel แต่สำหรับบทความนี้ เราจะใช้ Lighthouse
Lighthouse เป็นแพ็คเกจที่ฉันสร้างขึ้นเมื่อไม่กี่ปีที่ผ่านมา และเพิ่งได้รับการสนับสนุนที่น่าทึ่งจากชุมชนที่กำลังเติบโตรอบๆ ช่วยให้นักพัฒนาสามารถตั้งค่าเซิร์ฟเวอร์ GraphQL ได้อย่างรวดเร็วโดยใช้ Laravel โดยมีต้นแบบเพียงเล็กน้อย ในขณะที่ยังมีความยืดหยุ่นเพียงพอที่จะให้นักพัฒนาสามารถปรับแต่งเซิร์ฟเวอร์ให้เหมาะสมกับความต้องการของแทบทุกโครงการ
เริ่มต้นด้วยการดึงแพ็คเกจเข้าสู่โครงการของเรา:
$ composer require nuwave/lighthouse:"3.1.*"
ต่อไป มาเผยแพร่ไฟล์การกำหนดค่าของ Lighthouse:
$ php artisan vendor:publish --provider="Nuwave\Lighthouse\LighthouseServiceProvider" --tag=config
หมายเหตุ: คุณยังสามารถเลือกที่จะเผยแพร่ไฟล์สคีมาเริ่มต้นของ Lighthouse ได้โดยเพียงแค่ลบ --tag=config
ตัวเลือก แต่สำหรับวัตถุประสงค์ของบทความนี้ เราจะสร้างไฟล์สคีมาตั้งแต่ต้น
ถ้าเราดูที่ไฟล์ config/lighthouse.php
คุณจะสังเกตเห็นการตั้งค่าที่ใช้ในการลงทะเบียนไฟล์สคีมาของเรากับ Lighthouse:
'schema' => [ 'register' => base_path('graphql/schema.graphql'), ],
มาสร้างไฟล์สคีมาและตั้งค่าประเภทออบเจกต์ผู้ใช้และคิวรีของเรากัน:
$ mkdir graphql $ touch ./graphql/schema.graphql /graphql/schema.graphql type User { id: ID! name: String! email: String! } type Query { user(id: ID! @eq): User @find users: [User!]! @all }
คุณจะสังเกตเห็นว่าสคีมาของเราดูคล้ายกับที่เรากำหนดไว้ก่อนหน้านี้ ยกเว้นว่าเราได้เพิ่มตัวระบุที่เรียกว่าคำสั่งสคีมา
สละเวลาสักครู่เพื่อแยกย่อยสคีมาที่กำหนดไว้ของเรา คำจำกัดความแรกของเราคือ ประเภทวัตถุที่ เรียกว่า User
ซึ่งมีความสัมพันธ์กับรูปแบบวาทศิลป์ของ App\User
เรากำหนด id
name
และ email
เป็นฟิลด์ที่สามารถสอบถามจากโมเดล User
ของเรา อีกทางหนึ่งหมายความว่าคอลัมน์ password
, created_at
และ updated_at
เป็นฟิลด์ที่ไม่สามารถสืบค้นจาก API ของเราได้
ต่อไปเรามีประเภทการ Query
ซึ่งเป็นจุดเริ่มต้นใน API ของเราและสามารถใช้เพื่อค้นหาข้อมูล ฟิลด์แรกของเราคือฟิลด์ users
ซึ่งส่งคืนอาร์เรย์ของประเภทอ็อบเจ็กต์ User
คำสั่ง @all
บอกให้ Lighthouse เรียกใช้การสืบค้น Eloquent โดยใช้โมเดล User
ของเราและรับผลลัพธ์ทั้งหมด นี่จะเหมือนกับการรันสิ่งต่อไปนี้:
$users = \App\User::all();
หมายเหตุ: Lighthouse รู้จักมองหาโมเดลในเนมสเปซ \App\User
เนื่องจากตัวเลือก namespaces
ที่กำหนดไว้ในไฟล์การกำหนดค่า
ฟิลด์ที่กำหนดที่สองของเราในประเภทการสืบค้นของเราคือ call user
ซึ่งใช้ id
เป็นอาร์กิวเมนต์และส่งกลับประเภทอ็อบเจ็กต์ User
เดียว เรายังได้เพิ่มสองคำสั่งเพื่อช่วยให้ Lighthouse สร้างการสืบค้นข้อมูลให้เราโดยอัตโนมัติและส่งคืนโมเดล User
เดียว คำสั่ง @eq
บอกให้ Lighthouse เพิ่มตำแหน่งในคอลัมน์ id
ของเรา และคำสั่ง @find
สั่งให้ Lighthouse ส่งคืนผลลัพธ์เดียว ในการเขียนแบบสอบถามนี้โดยใช้ตัวสร้างแบบสอบถามของ Laravel จะมีลักษณะดังนี้:
$user = \App\User::where('id', $args['id'])->first();
การสืบค้น GraphQL API ของเรา
ตอนนี้เรามีข้อมูลเชิงลึกเล็กน้อยว่า Lighthouse ใช้สคีมาของเราในการสร้างการสืบค้นอย่างไร ให้เรียกใช้เซิร์ฟเวอร์ของเราและเริ่มสืบค้นข้อมูล เราจะเริ่มต้นด้วยการเรียกใช้เซิร์ฟเวอร์ของเรา:
$ php artisan serve Laravel development server started: <http://127.0.0.1:8000>
ในการสอบถามจุดปลาย GraphQL คุณสามารถเรียกใช้คำสั่ง cURL ในเทอร์มินัลหรือไคลเอนต์มาตรฐาน เช่น บุรุษไปรษณีย์ อย่างไรก็ตาม เพื่อให้ได้รับประโยชน์อย่างเต็มที่จาก GraphQL (เช่น การเติมข้อความอัตโนมัติ การเน้นข้อผิดพลาด เอกสารประกอบ ฯลฯ เราจะใช้ GraphQL Playground (เผยแพร่การดาวน์โหลดที่นี่)
เมื่อเริ่มต้น Playground ให้คลิกที่แท็บ "URL Endpoint" แล้วพิมพ์ http://localhost:8000/graphql เพื่อชี้ GraphQL Playground ไปที่เซิร์ฟเวอร์ของเรา ทางด้านซ้ายของตัวแก้ไข เราสามารถสืบค้นข้อมูลของเราได้ ดังนั้นเรามาเริ่มด้วยการขอผู้ใช้ทั้งหมดที่เรา seed ฐานข้อมูลด้วย:
{ users { id email name } }
เมื่อคุณกดปุ่มเล่นตรงกลาง IDE (หรือคลิก Ctrl+Enter ) คุณจะเห็นเอาต์พุต JSON ของเซิร์ฟเวอร์ของเราทางด้านขวา ซึ่งจะมีลักษณะดังนี้:
{ "data": { "users": [ { "id": "1", "email": "[email protected]", "name": "Carolyn Powlowski" }, { "id": "2", "email": "[email protected]", "name": "Elouise Raynor" }, { "id": "3", "email": "[email protected]", "name": "Mrs. Dejah Wiza" }, ... ] } }
หมายเหตุ: เนื่องจากเราใช้ Faker ในการ seed ฐานข้อมูลของเรา ข้อมูลในฟิลด์ email
และ name
จะแตกต่างกัน
ตอนนี้ เรามาลองค้นหาผู้ใช้คนเดียวกัน:
{ user(id: 1) { email name } }
และเราจะได้รับผลลัพธ์ต่อไปนี้สำหรับผู้ใช้คนเดียว:
{ "data": { "user": { "email": "[email protected]", "name": "Carolyn Powlowski" } } }
การสืบค้นข้อมูลแบบนี้เป็นสิ่งที่ดีในการเริ่มต้น แต่ไม่น่าเป็นไปได้อย่างยิ่งที่คุณจะอยู่ในโครงการที่คุณเคยต้องการสืบค้นข้อมูล ทั้งหมด ของคุณ ดังนั้นเรามาลองเพิ่มการแบ่งหน้ากัน เมื่อดูคำสั่งในตัวที่หลากหลายของ Lighthouse เรามีคำสั่ง @paginate
ที่พร้อมให้เราใช้งาน ดังนั้นเรามาอัปเดตออบเจกต์การสืบค้นของสคีมาดังนี้:
type Query { user(id: ID! @eq): User @find users: [User!]! @paginate }
หากเราโหลด GraphQL Playground ใหม่ ( Ctrl/Cmd + R ) และลองค้นหา users
ของเราอีกครั้ง คุณจะสังเกตเห็นว่าเราได้รับข้อความแสดงข้อผิดพลาดที่ระบุว่า Cannot query field "id" on type "UserPaginator"
เกิดอะไรขึ้น เบื้องหลัง Lighthouse จัดการสคีมาของเราเพื่อให้เราได้ชุดของผลลัพธ์ที่มีการแบ่งหน้า และทำโดยการเปลี่ยนประเภทการส่งคืนของฟิลด์ users
ของเรา

มาดูกันดีกว่าโดยการตรวจสอบสคีมาของเราในแท็บ "เอกสาร" ของ GraphQL Playground หากคุณดูที่ฟิลด์ users
ระบบจะส่งคืน UserPaginator
ซึ่งส่งคืนอาร์เรย์ของผู้ใช้และประเภท PaginatorInfo
ที่ Lighthouse กำหนด:
type UserPaginator { paginatorInfo: PaginatorInfo! data: [User!]! } type PaginatorInfo { count: Int! currentPage: Int! firstItem: Int hasMorePages: Boolean! lastItem: Int lastPage: Int! perPage: Int! total: Int! }
หากคุณคุ้นเคยกับการแบ่งหน้าในตัวของ Laravel ฟิลด์ที่มีอยู่ในประเภท PaginatorInfo
อาจจะดูคุ้นเคยสำหรับคุณมาก ดังนั้น ในการสอบถามผู้ใช้สองคน รับจำนวนผู้ใช้ทั้งหมดในระบบ และตรวจสอบว่าเรามีหน้าที่ต้องวนซ้ำอีก เราจะส่งข้อความค้นหาต่อไปนี้:
{ users(count:2) { paginatorInfo { total hasMorePages } data { id name email } } }
ซึ่งจะให้คำตอบแก่เราดังต่อไปนี้:
{ "data": { "users": { "paginatorInfo": { "total": 11, "hasMorePages": true }, "data": [ { "id": "1", "name": "Carolyn Powlowski", "email": "[email protected]" }, { "id": "2", "name": "Elouise Raynor", "email": "[email protected]" }, ] } } }
ความสัมพันธ์
โดยทั่วไป เมื่อพัฒนาแอปพลิเคชัน ข้อมูลส่วนใหญ่ของคุณมีความเกี่ยวข้องกัน ในกรณีของเรา User
สามารถเขียน Articles
ได้มากมาย ดังนั้นให้เพิ่มความสัมพันธ์นั้นกับประเภทผู้ใช้ของเราและกำหนดประเภท Article
ของเรา:
type User { id: ID! name: String! email: String! articles: [Article!]! @hasMany } type Article { id: ID! title: String! content: String! }
ที่นี่ เราใช้ Lighthouse อื่นที่มีสคีมา directive @hasMany
ซึ่งบอก Lighthouse โมเดล User
ของเรามี \Illuminate\Database\Eloquent\Relations\HasMany
กับโมเดล Article
ตอนนี้ เรามาสอบถามความสัมพันธ์ที่กำหนดขึ้นใหม่ของเรากัน:
{ user(id:1) { articles { id title } } }
สิ่งนี้จะให้คำตอบแก่เราดังต่อไปนี้:
{ "data": { "user": { "articles": [ { "id": "1", "title": "Aut velit et temporibus ut et tempora sint." }, { "id": "2", "title": "Voluptatem sed labore ea voluptas." }, { "id": "3", "title": "Beatae sit et maxime consequatur et natus totam." }, { "id": "4", "title": "Corrupti beatae cumque accusamus." }, { "id": "5", "title": "Aperiam quidem sit esse rem sed cupiditate." } ] } } }
สุดท้าย เรามาย้อนความสัมพันธ์ของเราและเพิ่มความสัมพันธ์ระหว่าง author
กับประเภทอ็อบเจกต์ Article
ของเราโดยใช้คำสั่งสคีมา @belongsTo
ของ Lighthouse รวมถึงอัปเดต Query
ของเรา:
type Article { id: ID! title: String! content: String! author: User! @belongsTo(relation: "user") } type Query { user(id: ID! @eq): User @find users: [User!]! @paginate article(id: ID! @eq): Article @find articles: [Article!]! @paginate }
คุณจะเห็นว่าเราได้เพิ่มอาร์กิวเมนต์ relation
ที่เป็นทางเลือกให้กับคำสั่ง @belongsTo
การดำเนินการนี้จะบอกให้ Lighthouse ใช้ความสัมพันธ์ user
ของโมเดล Articles
และกำหนดให้กับฟิลด์ author
ตอนนี้ เรามาค้นหารายชื่อบทความและหาผู้แต่งที่เกี่ยวข้องกัน:
{ articles(count:2) { paginatorInfo { total hasMorePages } data { id title author { name email } } } }
และเราควรได้รับสิ่งต่อไปนี้จากเซิร์ฟเวอร์ของเรา:
{ "data": { "articles": { "paginatorInfo": { "total": 55, "hasMorePages": true }, "data": [ { "id": "1", "title": "Aut velit et temporibus ut et tempora sint.", "author": { "name": "Carolyn Powlowski", "email": "[email protected]" } }, { "id": "2", "title": "Voluptatem sed labore ea voluptas.", "author": { "name": "Carolyn Powlowski", "email": "[email protected]" } } ] } } }
การกลายพันธุ์ของ GraphQL
ตอนนี้เราสามารถสืบค้นข้อมูลของเราได้แล้ว มาสร้างการกลายพันธุ์เพื่อสร้างผู้ใช้และบทความใหม่ เราจะเริ่มต้นด้วยรูปแบบผู้ใช้ของเรา:
type Mutation { createUser( name: String! email: String! @rules(apply: ["email", "unique:users"]) password: String! @bcrypt @rules(apply: ["min:6"]) ): User @create }
ตอนนี้ เรามาทำลายนิยามสคีมานี้กัน เราได้สร้างการกลายพันธุ์ที่เรียกว่า createUser
ซึ่งรับสามอาร์กิวเมนต์ ( name
, email
และ password
) เราใช้คำสั่ง @rules
กับอาร์กิวเมนต์ email
และ password
ของเรา สิ่งนี้อาจดูคุ้นเคยเล็กน้อยเพราะคล้ายกับตรรกะการตรวจสอบที่ Laravel จัดเตรียมไว้สำหรับคอนโทรลเลอร์
ต่อไป เราได้แนบคำสั่ง @bcrypt
เข้ากับฟิลด์ password
ของเรา การดำเนินการนี้จะเข้ารหัสรหัสผ่านก่อนที่จะส่งไปยังโมเดลที่สร้างขึ้นใหม่
สุดท้าย เพื่อช่วยเราสร้างโมเดลใหม่ Lighthouse ได้จัดเตรียมคำสั่ง @create
schema ซึ่งจะรับข้อโต้แย้งที่เรากำหนดและสร้างโมเดลใหม่ การใช้ตรรกะเดียวกันในคอนโทรลเลอร์จะมีลักษณะดังนี้:
namespace App\Http\Controllers; use Illuminate\Http\Request; class UserController extends Controller { /** * Create a new user. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $data = $this->validate($request, [ 'email' => ['email', 'unique:users'], 'password' => ['min:6'] ]); $user = \App\User::create($data); return response()->json(['user' => $user]); } }
ตอนนี้เราได้ตั้งค่าฟิลด์การกลายพันธุ์ของ createUser แล้ว ให้เรียกใช้ใน GraphQL Playground ด้วยสิ่งต่อไปนี้:
mutation { createUser( name:"John Doe" email:"[email protected]" password: "secret" ) { id name email } }
เราควรจะได้รับผลลัพธ์ต่อไปนี้:
{ "data": { "createUser": { "id": "12", "name": "John Doe", "email": "[email protected]" } } }
การตรวจสอบและการอนุญาต GraphQL
เนื่องจากเราจำเป็นต้องเพิ่ม user_id
ให้กับโมเดล Article
ของเรา ตอนนี้จึงเป็นเวลาที่ดีในการตรวจสอบสิทธิ์และการอนุญาตใน GraphQL/Lighthouse
ในการตรวจสอบสิทธิ์ผู้ใช้ เราจำเป็นต้องจัดเตรียม api_token
ให้พวกเขา ดังนั้นเรามาสร้างการกลายพันธุ์เพื่อจัดการกับสิ่งนั้น แล้วเราจะเพิ่มคำสั่ง @field
เพื่อชี้ Lighthouse ไปยังตัวแก้ไขแบบกำหนดเองซึ่งจะจัดการกับตรรกะ เราตั้งค่าตัวแก้ไขในรูปแบบเดียวกับการกำหนดตัวควบคุมใน resolver
โดยใช้อาร์กิวเมนต์ตัวแก้ไข
ด้วยคำสั่ง @field
ที่กำหนดไว้ด้านล่าง เรากำลังแจ้ง Lighthouse เมื่อมีการเรียกใช้การกลายพันธุ์การ login
ใช้วิธี createToken
ในคลาส App\GraphQL\Mutations\AuthMutator
ของเรา:
type Mutation { # ... login( email: String! password: String! ): String @field(resolver: "AuthMutator@resolve") }
หมายเหตุ: คุณไม่จำเป็นต้องรวมเนมสเปซทั้งหมดที่นี่ ในไฟล์กำหนดค่า lighthouse.php
คุณจะเห็นว่าเรามีเนมสเปซที่กำหนดไว้สำหรับการกลายพันธุ์ของเราที่ตั้งค่าเป็น App\\GraphQL\\Mutations
แล้ว อย่างไรก็ตาม คุณสามารถใช้เนมสเปซแบบเต็มได้หากต้องการ
ลองใช้ตัวสร้างของ Lighthouse เพื่อสร้างคลาส mutator ใหม่:
$ php artisan lighthouse:mutation AuthMutator
ต่อไป มาอัปเดตฟังก์ชันตัวแก้ไขของเราดังนี้:
namespace App\GraphQL\Mutations; use Illuminate\Support\Arr; use Illuminate\Support\Str; use Illuminate\Support\Facades\Auth; use GraphQL\Type\Definition\ResolveInfo; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; class AuthMutator { /** * Return a value for the field. * * @param null $rootValue Usually contains the result returned from the parent field. In this case, it is always `null`. * @param mixed[] $args The arguments that were passed into the field. * @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context Arbitrary data that is shared between all fields of a single query. * @param \GraphQL\Type\Definition\ResolveInfo $resolveInfo Information about the query itself, such as the execution state, the field name, path to the field from the root, and more. * @return mixed */ public function resolve($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo) { $credentials = Arr::only($args, ['email', 'password']); if (Auth::once($credentials)) { $token = Str::random(60); $user = auth()->user(); $user->api_token = $token; $user->save(); return $token; } return null; } }
ตอนนี้เราได้ตั้งค่าตัวแก้ไขแล้ว มาทดสอบกันและลองรับโทเค็น API โดยใช้การกลายพันธุ์ต่อไปนี้ใน GraphQL Playground:
mutation { login(email:"[email protected]", password:"secret") }
เราควรจะได้รับโทเค็นที่ส่งกลับมาให้เราดังนี้:
{ "data": { "login": "VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE" } }
หมายเหตุ: อย่าลืมคัดลอกโทเค็นที่ส่งคืนจากการกลายพันธุ์ของล็อกอิน เพื่อให้เราสามารถใช้งานได้ในภายหลัง
ต่อไป มาเพิ่มฟิลด์แบบสอบถามซึ่งจะส่งคืนผู้ใช้ที่ตรวจสอบสิทธิ์เพื่อให้แน่ใจว่าตรรกะของเราใช้งานได้ เราจะเพิ่มฟิลด์ที่โทร me
และใช้คำสั่ง @auth
ของ Lighthouse เพื่อส่งคืนผู้ใช้ที่ได้รับการตรวจสอบสิทธิ์ในปัจจุบัน นอกจากนี้เรายังจะตั้งค่าอาร์กิวเมนต์ guard
เท่ากับ api
เนื่องจากเราจะตรวจสอบผู้ใช้อย่างไร
type Query { # ... me: User @auth(guard: "api") }
ตอนนี้ขอเรียกใช้แบบสอบถาม ใน GraphQL Playground คุณสามารถตั้งค่าส่วนหัวของคำขอได้โดยดับเบิลคลิกที่แท็บ "Http Headers" ที่ด้านล่าง เราเพิ่มส่วนหัวด้วยวัตถุ JSON ดังนั้นหากต้องการเพิ่มโทเค็นผู้ถือในแต่ละคำขอ คุณจะต้องเพิ่มสิ่งต่อไปนี้:
{ "Authorization": "Bearer VJCz1DCpmdvB9WatqvWbXBP2RN8geZQlrQatUnWIBJCdbAyTl3UsdOuio3VE" }
หมายเหตุ: แทนที่โทเค็นผู้ถือด้วยโทเค็นที่คุณได้รับเมื่อเรียกใช้แบบสอบถามการ เข้าสู่ระบบ
ตอนนี้มาเรียกใช้แบบสอบถาม me
:
{ me { email articles { id title } } }
เราควรจะได้ผลลัพธ์ที่มีลักษณะดังนี้:
{ "data": { "me": { "email": "[email protected]", "articles": [ { "id": "1", "title": "Rerum perspiciatis et quos occaecati exercitationem." }, { "id": "2", "title": "Placeat quia cumque laudantium optio voluptatem sed qui." }, { "id": "3", "title": "Optio voluptatem et itaque sit animi." }, { "id": "4", "title": "Excepturi in ad qui dolor ad perspiciatis adipisci." }, { "id": "5", "title": "Qui nemo blanditiis sed fugit consequatur." } ] } } }
มิดเดิลแวร์
ตอนนี้เราทราบแล้วว่าการรับรองความถูกต้องของเราทำงานอย่างถูกต้อง มาสร้างการกลายพันธุ์ครั้งสุดท้ายเพื่อสร้างบทความโดยใช้ผู้ใช้ที่รับรองความถูกต้องในปัจจุบัน เราจะใช้คำสั่ง @field
เพื่อชี้ Lighthouse ไปที่ตัวแก้ไขของเรา และเราจะรวมคำสั่ง @middleware
เพื่อให้แน่ใจว่าผู้ใช้เข้าสู่ระบบ
type Mutation { # ... createArticle(title: String!, content: String!): Article @field(resolver: "ArticleMutator@create") @middleware(checks: ["auth:api"]) }
ขั้นแรก ให้สร้างคลาสการกลายพันธุ์:
$ php artisan lighthouse:mutation ArticleMutator
ต่อไป มาอัปเดต mutator ด้วยตรรกะต่อไปนี้:
namespace App\GraphQL\Mutations; use Nuwave\Lighthouse\Support\Contracts\GraphQLContext; class ArticleMutator { /** * Return a value for the field. * * @param null $rootValue * @param mixed[] $args * @param \Nuwave\Lighthouse\Support\Contracts\GraphQLContext $context * @return mixed */ public function create($rootValue, array $args, GraphQLContext $context) { $article = new \App\Article($args); $context->user()->articles()->save($article); return $article; } }
หมายเหตุ: เราเปลี่ยนชื่อฟังก์ชัน resolve
เริ่มต้นเพื่อ create
คุณไม่จำเป็นต้องสร้างคลาสใหม่สำหรับตัวแก้ไขทุกตัว แต่คุณสามารถจัดกลุ่มตรรกะเข้าด้วยกันได้หากเหมาะสมกว่า
สุดท้าย ให้เรียกใช้การกลายพันธุ์ใหม่ของเราและตรวจสอบผลลัพธ์ อย่าลืมเก็บส่วนหัวการให้ Authorization
จากข้อความค้นหาก่อนหน้าของเราในแท็บ "ส่วนหัว HTTP":
mutation { createArticle( title:"Building a GraphQL Server with Laravel" content:"In case you're not currently familiar with it, GraphQL is a query language used to interact with your API..." ) { id author { id email } } }
เราควรจะได้รับผลลัพธ์ต่อไปนี้:
{ "data": { "createArticle": { "id": "56", "author": { "id": "1", "email": "[email protected]" } } } }
ห่อ
ในการสรุป เราได้ใช้ประโยชน์จาก Lighthouse เพื่อสร้างเซิร์ฟเวอร์ GraphQL สำหรับโครงการ Laravel ของเรา เราใช้คำสั่งสคีมาที่สร้างขึ้นบางส่วน สร้างการสืบค้นและการกลายพันธุ์ และจัดการการให้สิทธิ์และการตรวจสอบสิทธิ์
Lighthouse ช่วยให้คุณทำสิ่งต่างๆ ได้มากขึ้น (เช่น อนุญาตให้คุณสร้างคำสั่งสคีมาที่กำหนดเอง) แต่สำหรับวัตถุประสงค์ของบทความนี้ เรายึดติดอยู่กับพื้นฐาน และเราสามารถทำให้เซิร์ฟเวอร์ GraphQL ทำงานได้โดยใช้ต้นแบบเพียงเล็กน้อย
ครั้งต่อไปที่คุณต้องตั้งค่า API สำหรับแอปพลิเคชันมือถือหรือหน้าเดียว อย่าลืมพิจารณา GraphQL เป็นวิธีการสืบค้นข้อมูลของคุณ!