PHP Academy · درس

المحللات والطفرات والاشتراكات

اجلب البيانات وعدّلها عبر المحللات

الدرس 3 من 413 خطوة

المحللات والطفرات والاشتراكات درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.

المحللات هي موضع المنطق

يصف المخطط ما هو موجود؛ بينما تحدد المحللات كيفية إنتاج قيمة كل حقل. والمحلّل ليس سوى دالة قابلة للاستدعاء. أما Mutations فهي محللات تغيّر الحالة. وتبث Subscriptions القيم بمرور الوقت. يغطي هذا الدرس الأنواع الثلاثة ونموذج التنفيذ الذي يربط بينها.

توقيع المحلّل

يتلقى كل محلّل أربعة معاملات: ($objectValue, $args, $context, ResolveInfo $info).

  • $objectValue — القيمة التي حلّها الأصل (وتكون rootValue في المستوى الأعلى).
  • $args — وسائط الحقل.
  • $context — حالة مشتركة خاصة بالطلب (مقبض قاعدة البيانات، والمستخدم الحالي).
  • $info — بيانات AST/الحقل الوصفية (اسم الحقل، ومجموعة الاختيار، والمسار).
<?php
use GraphQL\Type\Definition\ResolveInfo;

$resolve = function ($objectValue, array $args, $context, ResolveInfo $info) {
    // $context['db'], $context['user'] set up per request
    return $context['db']->find($args['id']);
};

المحلّل الافتراضي

إذا لم توفّروا resolve، يقرأ المحلّل الافتراضي في graphql-php اسم الحقل من قيمة الأصل: مفتاحًا في مصفوفة، أو خاصية عامة، أو دالة get<Field>(). وهذا يعني أنكم غالبًا تستطيعون حل أنواع كائنات كاملة دون أي تعليمات تمهيدية، وذلك بإرجاع مصفوفات عادية أو DTOs من الأصل.

<?php
// Parent returns this array; child fields resolve by key automatically:
$user = [
    'id' => 1,
    'name' => 'Ada',
    'email' => 'ada@example.com',
];
// 'name' field -> $user['name'] with no explicit resolver needed
var_dump($user['name']);

تتسلسل المحللات من الأصل إلى الابن

يتم التنفيذ من الأعلى إلى الأسفل: يعيد محلّل Query.user مستخدمًا، فيصبح هذا المستخدم قيمة $objectValue الخاصة بـ User.posts، وتصبح النتيجة بدورها أصلًا لكل Post.title. ويُعد فهم هذا التسلسل أساسيًا — فهو الموضع الذي تظهر فيه مشكلة N+1 بالضبط (وسنتناولها في الدرس التالي).

<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$userType = new ObjectType([
    'name' => 'User',
    'fields' => fn() => [
        'id'    => Type::id(),
        'name'  => Type::string(),
        'posts' => [
            'type' => Type::listOf(Type::string()),
            // $user is the parent value resolved by Query.user
            'resolve' => fn($user) => Posts::titlesForUser($user['id']),
        ],
    ],
]);

إرجاع Promises (بشكل غير متزامن)

يمكن للمحللات إرجاع قيمة أو promise. يأتي graphql-php مع محوّل وعود متزامن؛ ومع محولات ReactPHP/Amp يمكن تأجيل الحل وتجميعه. وحتى بشكل متزامن، يتيح إرجاع كائنات Deferred للمنفّذ جمع العمل وتشغيله بعد مستوى الحل الحالي — وهي الآلية التي يُبنى عليها DataLoader.

<?php
use GraphQL\Deferred;

$resolve = function ($post) use ($authorBuffer) {
    $authorBuffer->add($post['author_id']);   // queue the id
    return new Deferred(function () use ($authorBuffer, $post) {
        $authorBuffer->loadOnce();             // one batched query
        return $authorBuffer->get($post['author_id']);
    });
};

تغيّر Mutations الحالة

إن Mutation ليس سوى نوع جذر يُسمى Mutation. وبحسب convention، تُنفّذ حقوله ذات المستوى الأعلى بالتتابع (لا بالتوازي) حتى تُرتّب الآثار الجانبية. وعادةً ما تُجمّع المدخلات داخل InputObjectType للحصول على توقيع واضح.

<?php
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\Type;

$createPostInput = new InputObjectType([
    'name' => 'CreatePostInput',
    'fields' => [
        'title' => Type::nonNull(Type::string()),
        'body'  => Type::string(),
    ],
]);

ربط نوع Mutation

يستقبل حقل العملية الطافرة كائن الإدخال باعتباره وسيطًا، ويعيد الكيان الذي أُنشئ (حتى يتمكن العملاء من قراءة الحقول في دورة طلب واستجابة واحدة). أجروا التحقق والتفويض داخل المحلّل، وألقوا استثناءً عند الفشل.

<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$mutationType = new ObjectType([
    'name' => 'Mutation',
    'fields' => [
        'createPost' => [
            'type' => $postType,
            'args' => ['input' => Type::nonNull($createPostInput)],
            'resolve' => function ($root, array $args, $context) {
                if (!$context['user']) {
                    throw new \RuntimeException('Unauthenticated');
                }
                return PostRepo::create($args['input'], $context['user']);
            },
        ],
    ],
]);

الأخطاء: الآمنة للعميل مقابل الداخلية

يخفي graphql-php رسائل الاستثناءات افتراضيًا، ويعرض Internal server error لتجنب تسريب التفاصيل الداخلية. ولعرض رسالة للعملاء، نفّذوا GraphQL\Error\ClientAware وأعيدوا true من isClientSafe(). أضيفوا رموزًا قابلة للقراءة آليًا عبر extensions.

<?php
use GraphQL\Error\ClientAware;

class ValidationError extends \RuntimeException implements ClientAware {
    public function isClientSafe(): bool { return true; }
    // older versions also used getCategory(): string
}

الاشتراكات: المفهوم

يتيح نوع الجذر Subscription للعملاء تلقي تدفق من النتائج عند وقوع الأحداث (مثل وصول رسالة جديدة أو تغيّر السعر). تحدد مواصفة GraphQL دلالات الاشتراكات، لكن graphql-php ينفّذ عملية واحدة في كل استدعاء — ولا يشغّل خادم مقابس طويل الأمد بنفسه. وعليكم توفير وسيلة النقل.

  • يحل graphql-php حمولة الاشتراك لكل حدث تدفعونه.
  • تنقل وسيلة النقل (WebSocket عبر Ratchet/Mercure/Pusher) الأحداث إلى العملاء.

بنية محلّل Subscription

عمليًا، تقسّمون الاشتراك إلى خطوة subscribe (تسجيل الاهتمام وإرجاع مصدر للأحداث) وخطوة resolve (تعيين كل حدث إلى حمولة الحقل). وتجمع العديد من حزم PHP بين graphql-php وMercure أو وسيط pub/sub؛ ويوضح المحلّل أدناه التعيين لكل حدث الذي يتولاه graphql-php.

<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$subscriptionType = new ObjectType([
    'name' => 'Subscription',
    'fields' => [
        'messageAdded' => [
            'type' => $messageType,
            'args' => ['channelId' => Type::nonNull(Type::id())],
            // graphql-php resolves each pushed event into the payload;
            // a WebSocket/Mercure transport drives when this runs.
            'resolve' => fn($event) => $event['message'],
        ],
    ],
]);

السياق هو قناة المصادقة وحقن الاعتمادات

يُنشأ المعامل الثالث للمحلّل، $context، مرة واحدة لكل طلب، ويُمرّر إلى كل محلّل. وهو المكان المناسب للمستخدم الذي تمت مصادقته، واتصال قاعدة البيانات، وDataLoaders الخاصة بكم. ويؤدي وضع المصادقة مركزيًا هنا إلى إبقاء المحللات بسيطة — فهي تسأل السياق عن هوية المستخدم بدلًا من إعادة اشتقاقها.

<?php
require 'vendor/autoload.php';

// Built once per HTTP request, passed to executeQuery():
$context = [
    'user' => authenticate($_SERVER['HTTP_AUTHORIZATION'] ?? ''),
    'db'   => $pdo,
];

$resolve = function ($root, array $args, array $context) {
    if ($context['user'] === null) {
        throw new \RuntimeException('Unauthenticated');
    }
    return $context['db']->find($args['id']);
};

اختبار سريع

كيف تجعلون رسالة الاستثناء ظاهرة لعملاء GraphQL؟

مراجعة

تعلّمتم جوهر تنفيذ GraphQL:

  • تستقبل المحللات ($value, $args, $context, $info)؛ ويقرأ المحلّل الافتراضي المفاتيح ودوال getter من الأصل.
  • يتسلسل الحل من الأصل إلى الابن — وهو مصدر مشكلة N+1.
  • يتيح إرجاع Deferred/الوعود إجراء التجميع.
  • تتكون Mutations من حقول جذرية متسلسلة تستخدم InputObjectType؛ وأجروا المصادقة والتحقق داخل المحلّل.
  • تحدد Subscriptions حل الحمولة، بينما توفرون أنتم وسيلة النقل.
  • يتحكم ClientAware في رسائل الأخطاء التي يمكن للعملاء رؤيتها.

التالي: القضاء على مشكلة N+1 باستخدام DataLoader.

البدء مجانًا

تعلم PHP مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
49
الدروس
195

الأسئلة الشائعة

هل درس «المحللات والطفرات والاشتراكات» مجاني؟

نعم — نص درس «المحللات والطفرات والاشتراكات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.

ماذا ستتعلم في «المحللات والطفرات والاشتراكات»؟

اجلب البيانات وعدّلها عبر المحللات تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟

لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «المحللات والطفرات والاشتراكات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟

نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. GraphQL مقابل REST
  2. بناء مخطط باستخدام graphql-php
  3. المحللات والطفرات والاشتراكات
  4. الأداء: N+1 وDataLoader
← العودة إلى PHP Academy