منصة API: GraphQL مع Laravel
منصة API: GraphQL مع Laravel
GraphQL هو بديل قوي لواجهات REST API يسمح للعملاء بطلب البيانات التي يحتاجونها بالضبط. يغطي هذا الدرس تنفيذ GraphQL في Laravel باستخدام Lighthouse PHP، وهو إطار عمل لخدمة GraphQL من تطبيقات Laravel.
تثبيت وتكوين Lighthouse
Lighthouse هي مكتبة GraphQL الأكثر شهرة لـ Laravel. توفر طريقة تصريحية لتعريف مخطط GraphQL الخاص بك وتتكامل بسلاسة مع Eloquent.
# تثبيت Lighthouse و GraphQL Playground
composer require nuwave/lighthouse
composer require mll-lab/laravel-graphql-playground
# نشر التكوين
php artisan vendor:publish --tag=lighthouse-config
php artisan vendor:publish --tag=lighthouse-schema
# التكوين في config/lighthouse.php
return [
'route' => [
'uri' => '/graphql',
'middleware' => [
\Nuwave\Lighthouse\Support\Http\Middleware\AcceptJson::class,
],
],
'schema' => [
'register' => base_path('graphql/schema.graphql'),
],
];
# سيكون GraphQL Playground متاحاً على /graphql-playground
تعريف مخطط GraphQL الخاص بك
المخطط هو العقد بين العميل والخادم. يحدد الاستعلامات والطفرات المتاحة وأنواع البيانات التي تعيدها.
# graphql/schema.graphql
"A datetime string with format `Y-m-d H:i:s`"
scalar DateTime @scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\DateTime")
type Query {
"الحصول على جميع المستخدمين"
users: [User!]! @all
"الحصول على مستخدم بواسطة المعرف"
user(id: ID! @eq): User @find
"البحث عن المستخدمين بالاسم"
searchUsers(name: String! @where(operator: "like")): [User!]! @all
"الحصول على منشورات مقسمة إلى صفحات"
posts(first: Int!, page: Int): PostPaginator @paginate
"الحصول على المستخدم المصادق عليه الحالي"
me: User @auth
}
type User {
id: ID!
name: String!
email: String!
posts: [Post!]! @hasMany
created_at: DateTime!
updated_at: DateTime!
}
type Post {
id: ID!
title: String!
content: String!
author: User! @belongsTo
comments: [Comment!]! @hasMany
created_at: DateTime!
updated_at: DateTime!
}
type Comment {
id: ID!
body: String!
post: Post! @belongsTo
user: User! @belongsTo
created_at: DateTime!
}
type PostPaginator {
data: [Post!]!
paginatorInfo: PaginatorInfo!
}
type PaginatorInfo {
currentPage: Int!
lastPage: Int!
total: Int!
hasMorePages: Boolean!
}
تنفيذ الطفرات
الطفرات هي طريقة GraphQL لتعديل البيانات. إنها مشابهة لـ POST و PUT و DELETE في واجهات REST API.
# graphql/schema.graphql
type Mutation {
"إنشاء مستخدم جديد"
createUser(input: CreateUserInput! @spread): User @create
"تحديث مستخدم موجود"
updateUser(id: ID!, input: UpdateUserInput! @spread): User @update
"حذف مستخدم"
deleteUser(id: ID! @whereKey): User @delete
"إنشاء منشور"
createPost(input: CreatePostInput!): Post @field(resolver: "PostMutator@create")
"تسجيل دخول المستخدم"
login(email: String!, password: String!): AuthPayload @field(resolver: "AuthMutator@login")
"تسجيل خروج المستخدم"
logout: LogoutResponse @field(resolver: "AuthMutator@logout") @guard
}
input CreateUserInput {
name: String! @rules(apply: ["required", "string", "max:255"])
email: String! @rules(apply: ["required", "email", "unique:users,email"])
password: String! @rules(apply: ["required", "min:8"]) @hash
}
input UpdateUserInput {
name: String @rules(apply: ["string", "max:255"])
email: String @rules(apply: ["email", "unique:users,email"])
}
input CreatePostInput {
title: String! @rules(apply: ["required", "max:255"])
content: String! @rules(apply: ["required"])
author_id: ID! @rules(apply: ["required", "exists:users,id"])
}
type AuthPayload {
access_token: String!
token_type: String!
expires_in: Int!
user: User!
}
type LogoutResponse {
message: String!
status: String!
}
إنشاء محللات مخصصة
للمنطق المعقد، ستحتاج إلى محللات مخصصة. هذه فئات PHP تتعامل مع استعلامات أو طفرات محددة.
<?php
// app/GraphQL/Mutations/PostMutator.php
namespace App\GraphQL\Mutations;
use App\Models\Post;
use Illuminate\Support\Facades\Auth;
class PostMutator
{
public function create($rootValue, array $args)
{
$user = Auth::user();
return Post::create([
'title' => $args['input']['title'],
'content' => $args['input']['content'],
'author_id' => $user->id,
'published_at' => now(),
]);
}
}
// app/GraphQL/Mutations/AuthMutator.php
namespace App\GraphQL\Mutations;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
class AuthMutator
{
public function login($rootValue, array $args)
{
$credentials = [
'email' => $args['email'],
'password' => $args['password'],
];
if (!Auth::attempt($credentials)) {
throw ValidationException::withMessages([
'email' => ['بيانات الاعتماد المقدمة غير صحيحة.'],
]);
}
$user = Auth::user();
$token = $user->createToken('api-token')->plainTextToken;
return [
'access_token' => $token,
'token_type' => 'Bearer',
'expires_in' => 3600,
'user' => $user,
];
}
public function logout()
{
Auth::user()->currentAccessToken()->delete();
return [
'message' => 'تم تسجيل الخروج بنجاح',
'status' => 'success',
];
}
}
// app/GraphQL/Queries/UserQuery.php
namespace App\GraphQL\Queries;
use App\Models\User;
class UserQuery
{
public function searchAdvanced($rootValue, array $args)
{
$query = User::query();
if (isset($args['name'])) {
$query->where('name', 'like', "%{$args['name']}%");
}
if (isset($args['email'])) {
$query->where('email', 'like', "%{$args['email']}%");
}
if (isset($args['created_after'])) {
$query->where('created_at', '>=', $args['created_after']);
}
return $query->get();
}
}
الاشتراكات للبيانات في الوقت الفعلي
تمكن اشتراكات GraphQL من الاتصال في الوقت الفعلي بين العميل والخادم باستخدام WebSockets.
# تثبيت Laravel WebSockets
composer require beyondcode/laravel-websockets
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider"
php artisan migrate
# graphql/schema.graphql
type Subscription {
"الاستماع للمنشورات الجديدة"
postCreated: Post
"الاستماع للتعليقات على منشور محدد"
commentAdded(postId: ID!): Comment
"الاستماع لإشعارات المستخدم"
notificationSent: Notification @guard
}
type Notification {
id: ID!
type: String!
title: String!
message: String!
user: User!
read_at: DateTime
created_at: DateTime!
}
<?php
// بث حدث اشتراك
use Nuwave\Lighthouse\Execution\Utils\Subscription;
// عند إنشاء منشور جديد
$post = Post::create($data);
Subscription::broadcast('postCreated', $post);
// عند إضافة تعليق
$comment = Comment::create($data);
Subscription::broadcast('commentAdded', $comment, [
'postId' => $comment->post_id,
]);
// تصفية بيانات الاشتراك
// app/GraphQL/Subscriptions/CommentAdded.php
namespace App\GraphQL\Subscriptions;
use App\Models\Comment;
use Illuminate\Support\Facades\Auth;
class CommentAdded
{
public function filter($subscriber, $value)
{
// إرسال فقط للمستخدمين الذين لديهم حق الوصول إلى المنشور
$comment = $value;
return $comment->post->isAccessibleBy(Auth::user());
}
public function resolve($rootValue, $args, $context)
{
return $rootValue;
}
}
التفويض في GraphQL
تنفيذ التفويض الصحيح أمر بالغ الأهمية لتأمين واجهة GraphQL API الخاصة بك.
# graphql/schema.graphql
type Query {
"استعلام للمسؤولين فقط"
allUsers: [User!]! @all @guard @can(ability: "viewAny", model: "App\\Models\\User")
"الحصول على المنشورات التي يمكن للمستخدم الوصول إليها"
myPosts: [Post!]! @all @guard @inject(context: "user.id", name: "author_id")
}
type Mutation {
"تحديث المنشور (المؤلف فقط)"
updatePost(id: ID!, input: UpdatePostInput!): Post
@update
@can(ability: "update", find: "id")
}
# التفويض المخصص في المحلل
<?php
// app/Policies/PostPolicy.php
namespace App\Policies;
use App\Models\Post;
use App\Models\User;
class PostPolicy
{
public function update(User $user, Post $post)
{
return $user->id === $post->author_id;
}
public function delete(User $user, Post $post)
{
return $user->id === $post->author_id || $user->isAdmin();
}
}
// app/GraphQL/Mutations/PostMutator.php
public function delete($rootValue, array $args)
{
$post = Post::findOrFail($args['id']);
$this->authorize('delete', $post);
$post->delete();
return $post;
}
تمرين 1: واجهة GraphQL API للمدونة
أنشئ واجهة GraphQL API كاملة لنظام مدونة مع الميزات التالية:
- الاستعلامات: قائمة المنشورات مع التقسيم إلى صفحات، البحث في المنشورات حسب العنوان/المحتوى، الحصول على منشور مع التعليقات
- الطفرات: إنشاء/تحديث/حذف المنشورات، إضافة تعليقات للمنشورات، الإعجاب/إلغاء الإعجاب بالمنشورات
- تنفيذ المصادقة باستخدام رموز Sanctum
- إضافة التفويض لضمان أن المؤلفين فقط يمكنهم تحرير منشوراتهم
- إنشاء اشتراك للتعليقات الجديدة على المنشورات
تمرين 2: واجهة API لمنتجات التجارة الإلكترونية
ابنِ واجهة GraphQL API لمنصة تجارة إلكترونية:
- حدد الأنواع للمنتجات والفئات والطلبات والمستخدمين
- نفذ استعلامات للبحث عن المنتجات مع التصفية (نطاق السعر، الفئة، التقييم)
- أنشئ طفرات لإدارة السلة (إضافة إلى السلة، تحديث الكمية، الدفع)
- أضف تحديثات المخزون في الوقت الفعلي باستخدام الاشتراكات
- نفذ التفويض على مستوى الحقل (إخفاء الأسعار للزوار)
تمرين 3: GraphQL للشبكة الاجتماعية
طور واجهة GraphQL API لشبكة اجتماعية مع:
- ملفات تعريف المستخدمين مع المنشورات والمتابعين والمتابَعين
- استعلام خلاصة الأخبار الذي يعيد منشورات من المستخدمين المتابعين
- طفرات للمتابعة/إلغاء المتابعة، إنشاء منشورات، الإعجاب/التعليق
- اشتراك الإشعارات في الوقت الفعلي
- تنفيذ تقسيم إلى صفحات يعتمد على المؤشر للتمرير اللانهائي
- إضافة تحديد المعدل لمنع البريد المزعج