بناء خادم 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); } }
بذر قاعدة البيانات
الآن بعد أن تم إعداد نماذجنا وعمليات الترحيل ، فلنبدأ في نشر قاعدة البيانات الخاصة بنا. سنبدأ بإنشاء بعض فئات المصدر 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 وخادم 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
هي حقول لا يمكن الاستعلام عنها من واجهة برمجة التطبيقات الخاصة بنا.
بعد ذلك ، لدينا نوع Query
الخاص بنا والذي يعد نقطة دخول إلى واجهة برمجة التطبيقات الخاصة بنا ويمكن استخدامه للاستعلام عن البيانات. حقلنا الأول هو حقل users
الذي يُرجع مصفوفة من أنواع كائنات User
. يخبر التوجيهall Lighthouse أن يقوم بتشغيل استعلام @all
، باستخدام نموذج User
الخاص بنا والحصول على جميع النتائج. سيكون هذا مماثلاً لتشغيل ما يلي:
$users = \App\User::all();
ملاحظة: يعرف Lighthouse أنه يبحث عن نموذج في مساحة الاسم \App\User
بسبب خيار namespaces
المحدد في ملف التكوين الخاص به.
الحقل الثاني المحدد في نوع الاستعلام الخاص بنا هو user
المكالمة ، والذي يأخذ id
كوسيطة ويعيد نوع كائن User
واحد. لقد أضفنا أيضًا توجيهين لمساعدة Lighthouse تلقائيًا في إنشاء استعلام لنا وإرجاع نموذج User
واحد. يخبر التوجيه @eq
Lighthouse أن يضيف مكانًا في عمود id
الخاص بنا ، ويوجه التوجيه @find
Lighthouse لإرجاع نتيجة واحدة. لكتابة هذا الاستعلام باستخدام منشئ الاستعلام في Laravel ، سيبدو كما يلي:
$user = \App\User::where('id', $args['id'])->first();
الاستعلام عن واجهة برمجة تطبيقات GraphQL الخاصة بنا
الآن بعد أن أصبح لدينا القليل من البصيرة حول كيفية استخدام Lighthouse لمخططنا لإنشاء استعلامات ، فلنقم بتشغيل خادمنا ونبدأ في الاستعلام عن البيانات. سنبدأ بتشغيل خادمنا:
$ php artisan serve Laravel development server started: <http://127.0.0.1:8000>
للاستعلام عن نقطة نهاية GraphQL ، يمكنك تشغيل أمر cURL في المحطة أو عميل قياسي مثل Postman. ومع ذلك ، للحصول على الفوائد الكاملة لـ GraphQL (مثل الإكمال التلقائي ، وتحديد الأخطاء ، والتوثيق ، وما إلى ذلك ، سنستخدم GraphQL Playground (تنزيلات الإصدار هنا).
عند بدء Playground ، انقر فوق علامة التبويب "URL Endpoint" ، واكتب http: // localhost: 8000 / graphql لتوجيه GraphQL Playground إلى خادمنا. على الجانب الأيسر من المحرر ، يمكننا الاستعلام عن بياناتنا ، لذا فلنبدأ بسؤال جميع المستخدمين الذين قمنا بتزويد قاعدة البيانات بالبذر:
{ 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 لبذر قاعدة البيانات الخاصة بنا ، فإن البيانات الموجودة في حقلي 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! }
هنا ، نستخدم توجيه مخطط آخر مقدم من @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
الخاص بنا باستخدام توجيه مخطط Query
بالإضافة إلى تحديث @belongsTo
:
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
التوجيهfield لتوجيه Lighthouse إلى محلل مخصص سيتعامل مع المنطق. قمنا بتعيين المحلل في نفس نمط تعريف وحدة التحكم في Laravel باستخدام وسيطة 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 لإنشاء فئة طفرات جديدة:
$ 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'sauth لإرجاع المستخدم المصادق عليه حاليًا. سنقوم أيضًا بتعيين وسيطة 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
بعد ذلك ، لنقم بتحديث المتحور بالمنطق التالي:
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 وتشغيله باستخدام نموذج معياري قليل إلى حد ما.
في المرة التالية التي تحتاج فيها إلى إعداد واجهة برمجة تطبيقات لتطبيق محمول أو تطبيق أحادي الصفحة ، تأكد من اعتبار GraphQL وسيلة للاستعلام عن بياناتك!