0Pricing
PHP Academy · บทเรียน

การสร้างสคีมาด้วย graphql-php

กำหนดชนิดข้อมูลและสคีมาด้วย webonyx/graphql-php

การสร้างสคีมาด้วย graphql-php เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน

graphql-php: การใช้งานอ้างอิง

webonyx/graphql-php คือพอร์ต PHP โดยพฤตินัยของการใช้งานอ้างอิง GraphQL ซึ่งมีระบบชนิดข้อมูล ตัวแยกวิเคราะห์ ตัวตรวจสอบความถูกต้อง และตัวดำเนินการให้คุณ คุณกำหนด schema ได้ทั้งแบบ ใช้โค้ดโปรแกรม (ออบเจ็กต์ PHP) หรือจาก SDL (แนวทางกำหนด schema ก่อน) ในบทเรียนนี้ เราจะสร้าง schema ด้วยตนเอง เพื่อให้คุณเข้าใจอย่างชัดเจนว่าแต่ละส่วนทำหน้าที่อะไร

composer require webonyx/graphql-php

สเกลาร์และรีจิสทรีชนิดข้อมูล

ค่าทุกค่าใน GraphQL จะลงท้ายที่ สเกลาร์ เช่น 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 จะใช้รีโซลเวอร์เริ่มต้น ซึ่งอ่านคีย์อาร์เรย์หรือพร็อพเพอร์ตี/เมธอดสำหรับอ่านค่าที่ตรงกันจากค่าหลัก

<?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(),
    ],
]);

การห่อชนิดแบบไม่เป็นค่าว่างและรายการ

ชนิดที่ห่อหุ้มใช้แสดงความเป็นค่าว่างและจำนวนสมาชิก:

  • Type::nonNull(T) → T! (ไม่เป็นค่าว่างเลย)
  • Type::listOf(T) → [T] (เป็นรายการที่อาจเป็นค่าว่าง และสมาชิกภายในก็อาจเป็นค่าว่างได้)
  • [Post!]! = รายการที่ไม่เป็นค่าว่างและมีโพสต์ที่ไม่เป็นค่าว่างทุกตัว → nonNull(listOf(nonNull($postType)))

ต้องกำหนดส่วนนี้ให้ถูกต้อง เพราะนี่คือสัญญาเรื่องค่าว่างของ schema ที่ให้ไว้กับไคลเอ็นต์

<?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 เป็น โคลเชอร์ แทนอาร์เรย์ โคลเชอร์จะทำงานภายหลังเมื่อชนิดข้อมูลทั้งสองมีอยู่แล้ว จึงหลีกเลี่ยงปัญหาว่าอะไรควรถูกสร้างก่อน

<?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) ของรีโซลเวอร์ อาร์กิวเมนต์เองก็มีชนิดข้อมูลได้ อาจกำหนดให้ไม่เป็นค่าว่าง และอาจมีค่า 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 ราก

ทุก schema ต้องมีชนิด Query ราก ซึ่งเป็นจุดเริ่มต้นที่ไคลเอ็นต์ใช้เรียกข้อมูล ในที่นี้เราจะเปิดเผยฟิลด์ hello เพียงฟิลด์เดียว เพื่อให้สามารถดำเนินการได้ตั้งแต่ต้นจนจบ อาร์กิวเมนต์ตัวแรกของรีโซลเวอร์รากคือ rootValue ของ schema ซึ่งมักเป็น 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

ห่อชนิดคิวรีไว้ใน 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"}}

ทางเลือกแบบกำหนด Schema ก่อนด้วย BuildSchema

หากคุณต้องการใช้ SDL, BuildSchema::build() จะแยกวิเคราะห์สตริง schema ให้เป็น schema ที่เรียกใช้ได้ จากนั้นคุณจึงผูกรีโซลเวอร์แยกต่างหาก เช่น ฟังก์ชันเรียกกลับสำหรับรีโซลเวอร์ของฟิลด์ วิธีนี้ทำให้คำจำกัดความชนิดข้อมูลยังคงเป็นแบบประกาศ ขณะที่ตรรกะยังอยู่ใน 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 โดยอัตโนมัติก่อนดำเนินการ คุณยังสามารถตรวจสอบได้ว่า schema เองมีความสอดคล้องภายในหรือไม่ในเวลาสร้าง/ใน CI ด้วย $schema->assertValid() เพื่อค้นหาการพิมพ์ผิดและการอ้างอิงที่เสียหายก่อนนำไปใช้งาน ไม่ใช่รอจนถึงเวลารับคำขอ

<?php
use GraphQL\Type\Schema;

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

ค่าแจกแจงและสเกลาร์กำหนดเอง

นอกเหนือจากออบเจ็กต์แล้ว ชนิดข้อมูลอีกสองแบบนี้ช่วยเติมเต็ม schema ส่วนใหญ่ EnumType จำกัดฟิลด์ให้รับค่าแบบมีชื่อจากชุดที่กำหนดไว้เท่านั้น ส่วน CustomScalarType เปิดให้คุณกำหนดสเกลาร์เฉพาะโดเมน เช่น DateTime และอีเมล ด้วยตรรกะ 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 เป็นโคลเชอร์

ทบทวน

คุณได้สร้าง schema ตั้งแต่พื้นฐานดังนี้:

  • สเกลาร์และชนิดที่ห่อหุ้ม (nonNull, listOf) ใช้แสดงสัญญาเรื่องค่าว่าง/จำนวนสมาชิก
  • ObjectType ที่มีแผนผังฟิลด์ (หรือโคลเชอร์แบบประเมินภายหลัง) ใช้กำหนดโครงสร้างข้อมูล
  • รีจิสทรีชนิดข้อมูลที่จดจำค่าจะจัดการการอ้างอิงแบบวนรอบ
  • ฟิลด์รับ args ที่มีการระบุชนิดข้อมูล และชนิด Query รากคือจุดเริ่มต้น
  • GraphQL::executeQuery() ใช้ดำเนินการ ส่วน assertValid() ช่วยป้องกันปัญหาใน schema ระหว่าง CI

ถัดไป: รีโซลเวอร์ มิวเทชัน และการสมัครรับข้อมูล

คำถามที่พบบ่อย

บทเรียน “การสร้างสคีมาด้วย graphql-php” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การสร้างสคีมาด้วย graphql-php” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การสร้างสคีมาด้วย graphql-php”

กำหนดชนิดข้อมูลและสคีมาด้วย webonyx/graphql-php คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน

บทเรียน “การสร้างสคีมาด้วย graphql-php” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม

ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. GraphQL เทียบกับ REST
  2. การสร้างสคีมาด้วย graphql-php
  3. ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล
  4. ประสิทธิภาพ: N+1 และ DataLoader
← กลับไปที่ PHP Academy