0Pricing
PHP Academy · درس

بناء مخطط باستخدام graphql-php

عرّف الأنواع والمخطط باستخدام webonyx/graphql-php

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

graphql-php: التنفيذ المرجعي

تُعدّ webonyx/graphql-php المنفذ الفعلي في PHP للتنفيذ المرجعي لـ GraphQL. فهي توفر نظام الأنواع، والمحلّل، والمدقّق، والمنفّذ. ويمكنكم تعريف المخطط إما برمجيًا (باستخدام كائنات PHP) أو من خلال SDL (نهج المخطط أولًا). في هذا الدرس، سنبني مخططًا يدويًا حتى تفهموا بالضبط وظيفة كل جزء.

composer require webonyx/graphql-php

القيم scalar وسجل الأنواع

تنتهي كل قيمة في GraphQL إلى نوع scalar: Int وFloat وString وBoolean وID. توجد هذه الأنواع في graphql-php ضمن الواجهة Type. ونظرًا إلى أن أنواع الكائنات تشير إلى بعضها بعضًا (وإلى نفسها أيضًا)، فمن الأنماط الشائعة استخدام TypeRegistry ثابت يتذكر كل نوع مؤقتًا، بحيث لا يُبنى إلا مرة واحدة.

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

// Built-in scalars, returned as singletons:
var_dump(Type::int()->name);     // "Int"
var_dump(Type::string()->name);  // "String"
var_dump(Type::id()->name);      // "ID"
var_dump(Type::nonNull(Type::string())->toString()); // "String!"

تعريف ObjectType

يحتوي ObjectType على name وخريطة fields. ويصرّح كل حقل عن type الخاص به، ويمكنه اختياريًا تحديد دالة استدعاء resolve. وإذا حذفتم resolve، يستخدم graphql-php المحلّل الافتراضي، الذي يقرأ المفتاح المطابق في المصفوفة أو الخاصية/الدالة getter من قيمة الأصل.

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

$postType = new ObjectType([
    'name' => 'Post',
    'fields' => [
        'id'    => Type::nonNull(Type::id()),
        'title' => Type::nonNull(Type::string()),
        'body'  => Type::string(),
    ],
]);

تغليف Non-Null والقوائم

تعبّر الأنواع المغلّفة عن السماح بالقيمة null وعن التعددية:

  • Type::nonNull(T) → T! (لا تكون null مطلقًا).
  • Type::listOf(T) → [T] (قائمة قد تكون null، وقد تحتوي على عناصر null).
  • [Post!]! = قائمة غير null من منشورات غير null → nonNull(listOf(nonNull($postType))).

احرصوا على ضبط ذلك بشكل صحيح؛ فهذا هو عقد المخطط الخاص بكم بشأن القيم null، وهو العقد الذي يتعامل معه العملاء.

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

// [Post!]!  -- a required list whose elements are never null
$wrapped = Type::nonNull(Type::listOf(Type::nonNull(Type::string())));
echo $wrapped->toString(), "\n"; // [String!]!

الحقول الكسولة تحل المراجع الدائرية

يحتوي User على posts، ويحتوي Post على author (وهو User). ولإنشاء أنواع تشير إلى بعضها بعضًا، مرّروا fields باعتبارها closure بدلًا من مصفوفة. تُنفّذ closure بشكل كسول بعد إنشاء النوعين، متجاوزةً مشكلة الدجاجة والبيضة.

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

class Types {
    private static array $cache = [];

    public static function user(): ObjectType {
        return self::$cache['User'] ??= new ObjectType([
            'name' => 'User',
            'fields' => fn() => [           // lazy!
                'id'    => Type::nonNull(Type::id()),
                'name'  => Type::nonNull(Type::string()),
                'posts' => Type::listOf(self::post()),
            ],
        ]);
    }

    public static function post(): ObjectType {
        return self::$cache['Post'] ??= new ObjectType([
            'name' => 'Post',
            'fields' => fn() => [
                'id'     => Type::nonNull(Type::id()),
                'title'  => Type::nonNull(Type::string()),
                'author' => self::user(),    // back-reference
            ],
        ]);
    }
}

وسائط الحقول

يمكن للحقول استقبال وسائط. صرّحوا عنها ضمن المفتاح args؛ وستصل باعتبارها المعامل الثاني ($args) للمحلّل. وتكون الوسائط نفسها محددة النوع، ويمكن أن تكون غير قابلة لـ null، كما يمكن أن تحمل defaultValue.

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

$userField = [
    'type' => Type::string(),
    'args' => [
        'id'     => Type::nonNull(Type::id()),
        'locale' => ['type' => Type::string(), 'defaultValue' => 'en'],
    ],
    'resolve' => fn($root, array $args) => "user {$args['id']} ({$args['locale']})",
];

نوع Query الجذري

يحتاج كل مخطط إلى نوع جذر Query — وهو نقاط الدخول التي يمكن للعملاء البدء منها. نعرض هنا حقلًا واحدًا هو hello حتى نتمكن من تنفيذ العملية من البداية إلى النهاية. ويكون المعامل الأول للمحلّل الجذري هو rootValue الخاص بالمخطط (وغالبًا ما يكون null).

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

$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => [
        'hello' => [
            'type' => Type::string(),
            'args' => ['name' => Type::nonNull(Type::string())],
            'resolve' => fn($root, array $args) => 'Hello, ' . $args['name'],
        ],
    ],
]);

تجميع المخطط وتنفيذه

غلّفوا نوع الاستعلام داخل Schema، ثم مرّروا سلسلة استعلام إلى GraphQL::executeQuery(). ويمكن تحويل كائن النتيجة إلى مصفوفة { data, errors } المعتمدة باستخدام toArray().

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

use GraphQL\GraphQL;
use GraphQL\Type\Schema;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => [
        'hello' => [
            'type' => Type::string(),
            'args' => ['name' => Type::nonNull(Type::string())],
            'resolve' => fn($root, $args) => 'Hello, ' . $args['name'],
        ],
    ],
]);

$schema = new Schema(['query' => $queryType]);
$result = GraphQL::executeQuery($schema, '{ hello(name: "Ada") }');
echo json_encode($result->toArray());
// {"data":{"hello":"Hello, Ada"}}

بديل نهج المخطط أولًا باستخدام BuildSchema

إذا كنتم تفضّلون SDL، فإن BuildSchema::build() يحلّل سلسلة مخطط إلى مخطط قابل للتنفيذ. بعد ذلك، تربطون المحللات بشكل منفصل (مثلًا باستخدام دالة استدعاء لحل الحقول)، وبذلك تبقى تعريفات الأنواع تصريحية بينما يبقى المنطق في PHP.

<?php
use GraphQL\Utils\BuildSchema;

$sdl = <<<'GQL'
type Query {
  hello(name: String!): String
}
GQL;

$schema = BuildSchema::build($sdl);
// Provide resolvers via the executeQuery $fieldResolver argument
// or with a type config decorator.

تحققوا قبل النشر

يتحقق graphql-php تلقائيًا من صحة الاستعلامات الواردة مقابل المخطط قبل التنفيذ. ويمكنكم أيضًا التأكد من اتساق المخطط داخليًا أثناء البناء/التكامل المستمر باستخدام $schema->assertValid() — لاكتشاف الأخطاء الإملائية والمراجع المعطلة قبل النشر، لا وقت الطلب.

<?php
use GraphQL\Type\Schema;

/** @var Schema $schema */
$schema->assertValid(); // throws InvariantViolation on a broken schema
echo "schema OK\n";

التعدادات والقيم scalar المخصصة

إلى جانب الكائنات، يكمل نوعان من الأنواع معظم المخططات. يقيّد EnumType الحقل بمجموعة ثابتة من القيم المسماة. ويتيح لكم CustomScalarType تعريف أنواع scalar خاصة بالمجال (مثل DateTime وEmail) باستخدام منطق serialize/parseValue/parseLiteral الخاص بكم، بحيث تُتحقق صحة القيم وتُطبّع عند الحدود.

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

$statusEnum = new EnumType([
    'name' => 'PostStatus',
    'values' => [
        'DRAFT'     => ['value' => 0],
        'PUBLISHED' => ['value' => 1],
        'ARCHIVED'  => ['value' => 2],
    ],
]);
// A field typed as $statusEnum only accepts DRAFT/PUBLISHED/ARCHIVED.

اختبار سريع

لماذا تمرّرون fields باعتبارها closure؟

مراجعة

بنيتم مخططًا من الصفر:

  • تعبّر القيم scalar والأنواع المغلّفة (nonNull وlistOf) عن عقد القيم null والتعددية.
  • يعرّف ObjectType مع خريطة حقول (أو closure كسولة) البنى الخاصة بكم.
  • يتولى سجل الأنواع الذي يتذكر القيم مؤقتًا معالجة المراجع الدائرية.
  • تستقبل الحقول args محددة النوع؛ ويكون نوع الجذر Query نقطة الدخول.
  • ينفّذها GraphQL::executeQuery()، بينما يحمي assertValid() المخطط ضمن CI.

التالي: المحللات، والعمليات الطافرة، والاشتراكات.

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

هل درس «بناء مخطط باستخدام graphql-php» مجاني؟

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

ماذا ستتعلم في «بناء مخطط باستخدام graphql-php»؟

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

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

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

كم من الوقت يستغرق درس «بناء مخطط باستخدام graphql-php»؟

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

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

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

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

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