- 后端
- 微服务
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
導讀
本篇以 Hyperf 官方文件(docs/zh-tw/graphql.md)為主體,深入講解hyperf/graphql元件如何以「註解驅動」的方式快速建構 GraphQL 查詢服務。你將學會:透過composer require hyperf/graphql完成安裝、注入Schema並以一個控制器方法暴露 GraphQL 端點、用#[Query]定義查詢、用#[Type]/#[Field]將 PHP 類別對映為 GraphQL 型別,並理解元件底層(SchemaFactory、ConfigProvider、GraphQLMiddleware)的運作原理,讓你能夠基於同一套 Schema 輕鬆擴充微服務的資料查詢面。
元件概覽:對 GraphQLite 的抽象
Hyperf 的 GraphQL 元件(Hyperf\GraphQL)本質上是對 thecodingmachine/graphqlite 這套「註解驅動的 GraphQL Schema 建構器」進行的 Hyperf 化封裝。它的核心價值在於:你不需要手寫繁瑣的.graphqlSchema 檔案,只要在 PHP 類別與方法上掛上註解,GraphQLite 就會自動掃描並組裝出完整的 Schema,再由元件透過 Hyperf 的依賴注入容器(DI)將Schema實例注入到控制器中。
從當前倉庫的元件目錄結構(src/graphql)可以清楚看到,元件本身非常精簡,只包含三個核心檔案:
- ConfigProvider.php:向 Hyperf 註冊依賴綁定與註解掃描路徑;
- SchemaFactory.php:負責組裝並建立
Schema實例; - GraphQLMiddleware.php:提供一站式的 GraphQL HTTP 中介層(可選)。
真正的「型別掃描、註解解析、Schema 產生」等繁重工作,全部交由底層的 GraphQLite 完成。這種分層讓元件既保持輕量,又能繼承 GraphQLite 完整的能力。
安裝
在 Hyperf 專案根目錄執行:
composer require hyperf/graphql從元件的 composer.json 可以看到它的依賴與執行環境約束:
- PHP 版本要求:
php: >=8.2; - Hyperf 核心元件:
hyperf/codec(JSON 編解碼)、hyperf/context(協程上下文)、hyperf/contract(容器介面)、hyperf/di(依賴注入與註解掃描); - 底層引擎:
thecodingmachine/graphqlite: ^7.0 || ^8.1(同時相容 GraphQLite 7 與 8.x 兩個大版本); - 協程環境支援:
swow/psr7-plus; - 建議搭配(suggest):
hyperf/http-message與hyperf/http-server——若要使用GraphQLMiddleware,這兩個元件是必需的。
安裝完成後,元件會透過 ConfigProvider.php 自動完成以下初始化(無需手動註冊):
- 將
GraphQL\Type\Schema::class綁定到Hyperf\GraphQL\SchemaFactory,之後任何地方注入Schema都會取得由工廠建立好的實例; - 把元件自身目錄加入註解掃描路徑(
annotations.scan.paths),確保元件內的註解與類別能被 Hyperf 掃描到。
快速開始:建立第一個查詢
簡單查詢
建立一個控制器,注入Schema,並提供一個POST /graphql端點來接收並執行查詢。以下是官方文件給出的完整範例:
<?php namespace App\Controller; use GraphQL\GraphQL; use GraphQL\Type\Schema; use Hyperf\Di\Annotation\Inject; use Hyperf\GraphQL\Annotation\Query; use Hyperf\HttpServer\Annotation\Controller; use Hyperf\HttpServer\Annotation\PostMapping; use Hyperf\HttpServer\Contract\RequestInterface; #[Controller] class GraphQLController { #[Inject] protected Schema $schema; #[PostMapping(path: "/graphql")] public function test(RequestInterface $request) { $rawInput = $request->getBody()->getContents(); $input = json_decode($rawInput, true); $query = $input['query']; $variableValues = isset($input['variables']) ? $input['variables'] : null; return GraphQL::executeQuery($this->schema, $query, null, null, $variableValues)->toArray(); } #[Query] public function hello(string $name): string { return $name; } }這段程式碼的關鍵動作拆解如下:
#[Inject] protected Schema $schema;:透過 Hyperf DI 注入由SchemaFactory建立的 Schema 實例(綁定關係見 ConfigProvider.php);- 端點方法從請求 body 中讀取 JSON,取出
query欄位與可選的variables(查詢變數),再呼叫GraphQL::executeQuery()執行查詢,最後把結果轉成陣列作為響應; #[Query]註解標記的hello方法會被自動註冊為 GraphQL 的 Query 欄位,其參數string $name會成為查詢的入參。
發起查詢:
{ hello(name: "graphql") }響應:
{ "data": { "hello": "graphql" } }可以看到,hello方法只是把傳入的name原樣回傳,因此查詢與響應形成了一一對應的關係。
為什麼可以直接注入 Schema
初學者可能好奇:Schema是怎麼被建立、又是怎麼進入容器的?答案藏在兩個原始碼檔案裡。
首先,ConfigProvider.php 在依賴容器中註冊了:
'dependencies' => [ Schema::class => SchemaFactory::class, ],也就是說,每當需要GraphQL\Type\Schema時,容器會呼叫SchemaFactory的__invoke()方法取得實例。其次,SchemaFactory.php 的實作如下(核心邏輯):
public function __invoke(ContainerInterface $container) { $factory = new BaseSchemaFactory($container->get(CacheInterface::class), $container); if ($this->isVersion7($factory)) { $factory->addTypeNamespace('App'); $factory->addControllerNamespace('App'); } else { $factory->addNamespace('App'); } return $factory->createSchema(); }從這份原始碼可以推斷出幾個重要的底層事實:
- 快取依賴:
SchemaFactory需要容器提供Psr\SimpleCache\CacheInterface,也就是說,GraphQLite 的 Schema 建構過程會利用快取來避免每次請求都重新掃描與編譯 Schema——在協程框架中這對效能至關重要; - 掃描命名空間:元件預設把
App命名空間註冊為型別與控制器的掃描範圍(GraphQLite 7 使用addTypeNamespace+addControllerNamespace),這意味著你的查詢方法(如App\Controller\GraphQLController中的#[Query]方法)與型別類別(如App\Model\Product)都應放在App之下,才能被 Schema 建構器找到; - 版本相容:
isVersion7()透過檢查addNamespace()方法是否存在來判斷 GraphQLite 版本(SchemaFactory.php),自動適配 7.x 與 8.x 兩種 API,這與 composer.json 中thecodingmachine/graphqlite: ^7.0 || ^8.1的相容性宣告相互印證。
型別對映:把 PHP 類別變成 GraphQL 型別
真實專案中,查詢結果往往不是單一標量,而是一組結構化資料。hyperf/graphql支援透過#[Type]與#[Field]兩個註解,把普通 PHP 類別對映為 GraphQL Object 型別。
定義型別類別
以商品(Product)為例,建立一個型別類別:
<?php namespace App\Model; use Hyperf\GraphQL\Annotation\Type; use Hyperf\GraphQL\Annotation\Field; #[Type] class Product { protected $name; protected $price; public function __construct(string $name, float $price) { $this->name = $name; $this->price = $price; } #[Field] public function getName(): string { return $this->name; } #[Field] public function getPrice(): ?float { return $this->price; } }要點說明:
#[Type]標記該類別是一個 GraphQL 型別;#[Field]標記類別中的某個方法會成為該型別的一個欄位(Field),GraphQL 欄位名預設對應方法名去掉get前綴後的駝峰小寫形式,例如getName()對應欄位name、getPrice()對應欄位price;- 回傳型別宣告直接決定了 GraphQL 欄位的型別:
string對應String,?float對應「可為 null 的Float」——這正是 GraphQL 可空型別的語義,當商品價格可能缺失時非常實用。
在控制器中新增對應查詢
接著在GraphQLController中新增一個回傳Product型別的查詢方法:
<?php use App\Model\Product; use Hyperf\GraphQL\Annotation\Query; #[Query] public function product(string $name, float $price): Product { return new Product($name, $price); }這裡可以看到註解驅動的便利性:只要方法回傳型別是已註冊的#[Type]類別,GraphQLite 就會自動把它展開為多欄位的 Object 型別,無需任何額外設定。
查詢與響應
現在可以把兩個查詢放在同一次請求中:
{ hello(name: "graphql") product(name: "goods", price: 156.5) { name price } }響應:
{ "data": { "hello": "graphql", "product": { "name": "goods", "price": 156.5 } } }注意響應中的product物件只包含你在查詢中明確要求的name與price兩個欄位,這正是 GraphQL「按需取欄位」的核心特性:客戶端決定要哪些欄位,服務端據此裁剪資料。與傳統 REST 固定回傳整個物件的模式相比,這能有效減少冗餘傳輸,特別適合資料欄位多、查詢面複雜的微服務場景。
另一種接入方式:GraphQLMiddleware
除了在控制器中手動解析請求外,元件還內建了一個 GraphQLMiddleware.php,提供更「開箱即用」的接入方式。從原始碼可以看到它的完整行為(GraphQLMiddleware.php):
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { if (! $this->isGraphQL($request)) { return $handler->handle($request); } $input = $request->getParsedBody(); $query = $input['query']; $variableValues = $input['variables'] ?? null; $result = GraphQL::executeQuery($this->schema, $query, null, null, $variableValues); return ResponseContext::get()->setBody(new SwooleStream(Json::encode($result))); } protected function isGraphQL(ServerRequestInterface $request): bool { return $request->getUri()->getPath() === '/graphql'; }從中可以看出該中介層的設計特點:
- 路徑自動判別:只有當請求 URI 路徑恰好等於
/graphql時才處理,否則直接交由後續中介層/控制器處理,因此可以安全地註冊為全域中介層,不影響其他路由; - 直接讀取已解析 body:與控制器範例讀取原始 body 再
json_decode不同,中介層直接使用$request->getParsedBody(),依賴 Hyperf 對 JSON 請求體的自動解析; - 統一響應格式:執行結果透過
hyperf/codec的Json::encode編碼後寫入響應 Body,並使用SwooleStream包裹——這是 Hyperf 響應流的標準做法,也解釋了 composer.json 中依賴hyperf/codec、hyperf/context與hyperf/http-message的原因。
因此,接入 GraphQL 有兩種風格可選:控制器手動處理(適合需要自訂解析邏輯、鑑權或錯誤處理的場景)與GraphQLMiddleware 統一處理(適合標準化、減少樣板程式碼的場景)。官方文件以第一種方式為主線講解,你可以依照專案需求擇一使用。
環境與版本注意事項
在把範例落地到實際專案前,有幾點需要留意:
- PHP 8.2+ 是硬性要求:註解語法
#[...]與元件本身的型別系統都依賴較新的 PHP 特性,低於 8.2 的環境無法安裝; - 掃描命名空間預設為
App:從 SchemaFactory.php 可知,元件預設只掃描App命名空間下的控制器與型別。若你的業務程式碼放在其他命名空間,需要自行擴展SchemaFactory或調整程式碼結構; - 快取依賴:Schema 建構依賴
Psr\SimpleCache\CacheInterface,在 Hyperf 專案中通常由快取元件提供,請確保專案已具備可用的快取實作; - 中間件的 HTTP 元件依賴:若要使用
GraphQLMiddleware,需確認專案已安裝hyperf/http-message與hyperf/http-server(見 composer.json 的suggest段)。
結語
透過hyperf/graphql元件,Hyperf 專案可以繞過繁瑣的 GraphQL Schema DSL,直接用 PHP 註解建構出完整的查詢服務:#[Query]定義查詢入口、#[Type]/#[Field]定義型別結構,而Schema的建立、快取與依賴注入則由元件的ConfigProvider、SchemaFactory與 Hyperf DI 在幕後自動完成。文件中的簡單查詢與型別對映兩段範例,足以支撐起一個可運行的 GraphQL 端點;更進階的能力(如 Mutation、參數型別擴充、列舉、型別擴展等)都由底層 GraphQLite 提供,可以參考 GraphQLite 的官方文件按需學習。若想深入原始碼,建議從 SchemaFactory.php 與 GraphQLMiddleware.php 讀起,兩者合起來就是元件完整的執行路徑。
- 后端
- 微服务
【免费下载链接】hyperf
🚀 A coroutine framework that focuses on hyperspeed and flexibility. Building microservice or middleware with ease.
相关推荐
Hyperf 服務註冊實戰:基於 Consul 與 Nacos 的服務中心接入與自定義治理驅動
Hyperf 服務註冊實戰:基於 Consul 與 Nacos 的服務中心接入與自定義治理驅動 本指南以 Hyperf 的 service governance
后端微服务Hyperf 微服務架構實戰指南:從服務拆分理念到 RPC 生態落地
Hyperf 微服務架構實戰指南:從服務拆分理念到 RPC 生態落地 本篇技術指南以 Hyperf 官方文檔《微服務》為核心骨架,系統講解微服務架構的核心理念—
后端微服务Hyperf 查詢構造器完整實戰指南:從基礎查詢到悲觀鎖的資料庫操作全解析
Hyperf 查詢構造器完整實戰指南:從基礎查詢到悲觀鎖的資料庫操作全解析 導讀 Hyperf 的資料庫查詢構造器(Query Builder)是 db con
后端微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考