ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล
ดึงและเปลี่ยนข้อมูลผ่านตัวแก้ไข
ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล เป็นบทเรียน PHP Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน PHP Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
รีโซลเวอร์คือที่ที่ตรรกะทำงาน
schema อธิบายว่า มีอะไรอยู่บ้าง ส่วน รีโซลเวอร์ ตัดสินใจว่า จะสร้างค่าของแต่ละฟิลด์อย่างไร รีโซลเวอร์เป็นเพียงฟังก์ชันที่เรียกใช้ได้ มิวเทชันคือรีโซลเวอร์ที่เปลี่ยนสถานะ และการสมัครรับข้อมูลใช้สตรีมค่าตามเวลา บทเรียนนี้ครอบคลุมทั้งสามเรื่อง รวมถึงรูปแบบการดำเนินการที่เชื่อมโยงทั้งหมดเข้าด้วยกัน
ลายเซ็นของรีโซลเวอร์
รีโซลเวอร์ทุกตัวจะได้รับอาร์กิวเมนต์สี่ตัว: ($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>() นั่นหมายความว่าโดยมากคุณสามารถรีโซลฟ์ชนิดออบเจ็กต์ทั้งหมดได้โดยไม่ต้องเขียนโค้ดประกอบซ้ำ เพียงคืนอาร์เรย์ธรรมดาหรือออบเจ็กต์ถ่ายโอนข้อมูลจากค่าหลัก
<?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']),
],
],
]);
การคืนพรอมิส (แบบอะซิงโครนัส)
รีโซลเวอร์อาจคืนค่า หรือ พรอมิสก็ได้ 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']);
});
};
มิวเทชันเปลี่ยนสถานะ
มิวเทชัน เป็นเพียงชนิดรากที่ชื่อ Mutation ตามแบบแผน ฟิลด์ระดับบนสุดของมิวเทชันจะทำงานตามลำดับ ไม่ใช่ทำงานพร้อมกัน เพื่อให้ผลข้างเคียงเกิดตามลำดับ อินพุตมักถูกรวมไว้ใน 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(),
],
]);
การเชื่อมต่อชนิดมิวเทชัน
ฟิลด์มิวเทชันรับออบเจ็กต์อินพุตเป็นอาร์กิวเมนต์ และคืนเอนทิตีที่สร้างขึ้น เพื่อให้ไคลเอ็นต์อ่านฟิลด์กลับมาได้ภายในการเรียกครั้งเดียวกัน ให้ตรวจสอบความถูกต้องและสิทธิ์ภายในรีโซลเวอร์ และโยนข้อผิดพลาดเมื่อไม่ผ่าน
<?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
}
การสมัครรับข้อมูล: แนวคิด
ชนิดราก การสมัครรับข้อมูล เปิดให้ไคลเอ็นต์รับผลลัพธ์เป็นสตรีมเมื่อเกิดเหตุการณ์ เช่น มีข้อความใหม่หรือราคาขยับ ข้อกำหนด GraphQL กำหนด ความหมายเชิงพฤติกรรม ของการสมัครรับข้อมูล แต่ graphql-php จะดำเนินการเพียงหนึ่งการดำเนินการต่อการเรียกใช้เท่านั้น ไม่ได้ทำหน้าที่เป็นเซิร์ฟเวอร์ซ็อกเก็ตที่ทำงานต่อเนื่องยาวนานด้วยตัวเอง คุณต้องจัดเตรียมชั้นการส่งข้อมูลเอง
- graphql-php จะรีโซลฟ์ เพย์โหลด ของการสมัครรับข้อมูลสำหรับแต่ละเหตุการณ์ที่คุณส่งเข้ามา
- ชั้นการส่งข้อมูล (WebSocket ผ่าน Ratchet/Mercure/Pusher) จะส่งเหตุการณ์ไปยังไคลเอ็นต์
รูปแบบรีโซลเวอร์ของการสมัครรับข้อมูล
ในทางปฏิบัติ คุณจะแบ่งการสมัครรับข้อมูลออกเป็นขั้นตอน ลงทะเบียนความสนใจ (ลงทะเบียนความสนใจและคืนแหล่งเหตุการณ์) และขั้นตอน รีโซลฟ์ (แปลงแต่ละเหตุการณ์เป็นเพย์โหลดของฟิลด์) สแตก PHP จำนวนมากใช้ graphql-php ร่วมกับ Mercure หรือโบรกเกอร์เผยแพร่/สมัครรับข้อมูล รีโซลเวอร์ด้านล่างแสดงการแมปต่อเหตุการณ์ที่ 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'],
],
],
]);
บริบทคือช่องทางสำหรับการยืนยันตัวตนและ DI
อาร์กิวเมนต์ตัวที่สามของรีโซลเวอร์คือ $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)และรีโซลเวอร์เริ่มต้นจะอ่านคีย์/ตัวอ่านค่าจากค่าหลัก - การรีโซลฟ์ส่งต่อจากค่าหลักไปยังค่าลูก ซึ่งเป็นต้นเหตุของ N+1
- การคืน
Deferred/พรอมิสช่วยให้จัดงานเป็นชุดได้ - มิวเทชันคือฟิลด์รากที่ทำงานตามลำดับและใช้
InputObjectTypeโดยให้ตรวจสอบสิทธิ์/ความถูกต้องในรีโซลเวอร์ - การสมัครรับข้อมูลกำหนดการรีโซลฟ์เพย์โหลด ส่วนคุณเป็นผู้จัดเตรียมชั้นการส่งข้อมูล
ClientAwareควบคุมว่าข้อความข้อผิดพลาดใดที่ไคลเอ็นต์มองเห็นได้
ถัดไป: แก้ปัญหา N+1 ด้วย DataLoader
คำถามที่พบบ่อย
บทเรียน “ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส PHP Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส PHP Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล”
ดึงและเปลี่ยนข้อมูลผ่านตัวแก้ไข คุณปฏิบัติ PHP Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน PHP Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน PHP Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน PHP Academy นี้ได้ไหม
ได้ บทเรียน PHP Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- GraphQL เทียบกับ REST
- การสร้างสคีมาด้วย graphql-php
- ตัวแก้ไข การกลายพันธุ์ และการสมัครรับข้อมูล
- ประสิทธิภาพ: N+1 และ DataLoader