[TOC]
#### 定義
Interface Type
接口類型是一個抽象類型,它包含實現該接口的類型必須包含的某一組字段。
interface type 是 GraphQL\Type\Definition\InterfaceType(或其子類)的實例,它的構造函數接收數組配制參數。
~~~
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\Type;
$character = new InterfaceType([
'name' => 'Character',
'description' => 'A character in the Star Wars Trilogy',
'fields' => [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the character.',
],
'name' => [
'type' => Type::string(),
'description' => 'The name of the character.'
]
],
'resolveType' => function ($value) {
if ($value->type === 'human') {
return MyTypes::human();
} else {
return MyTypes::droid();
}
}
]);
~~~
#### 配制項
| 名字 | 類型 | 描述 |
| --- | --- | --- |
| name | `string` | `必填` Schema內這個接口類型的唯一命名 |
| fields | `array` | `必填` 接品實現者需要定義的字段列表 |
| description | `string` | 類型的純文本描述(例如通過GraphQL自動生成文檔) |
| resolveType | `callback` 返回對象類型的實例 | function($value, $context, GraphQL\Type\Definition\ResolveInfo $info)類型解析回調函數,接受從父字段解析器傳遞的值 $value,并需要返回該 $value 值的具體接口實現者(或實現者的類型名字) |
#### 實現接口
對象類型要實現接口,只需要將將其添加到該對象類型定義的interfaces的數組。
~~~
$humanType = new ObjectType([
'name' => 'Human',
'fields' => [
'id' => [
'type' => Type::nonNull(Type::string()),
'description' => 'The id of the character.',
],
'name' => [
'type' => Type::string(),
'description' => 'The name of the character.'
]
],
'interfaces' => [
$character
]
]);
~~~
注意,對象類型必須包括接口的所有字段及對應字段應該具有完全相同的類型(包括非空規范)和參數。
唯一例外的是當對象字段類型相比對應的接口字段類型更明確(參照接口字段的協變返回類型)。
#### 接口字段的協變返回類型
(Covariant return types for interface fields)
~~~
interface A {
field1: A
}
type B implements A {
field1: B
}
~~~
基類中某個函數在派生類中可以override,并且返回值得是基類中那個函數返回值的子類(來自java Covariant return types 解釋)
#### 共享接口字段
Sharing Interface fields
由于實現接口的每個對象類型必須具有接口的字段集 - 在對象類型中重用接口的字段定義通常是有意義的。
~~~
$humanType = new ObjectType([
'name' => 'Human',
'interfaces' => [
$character
],
'fields' => [
'height' => Type::float(),
$character->getField('id'),
$character->getField('name')
]
]);
~~~
在這種情況下,字段定義僅創建一次(作為接口類型的一部分),然后由所有接口實現者重新使用。 它可以節省幾微秒,千字節,最重要的是保證了接口字段定義和實現者總是保持同步。
共享接口字段在下面兩種情況下它能很好的解決問題。
* 如果字段解析器算法 對所有接口實現者是相同的,您可以簡單的將字段解析器(resolve )配制項添加到接口本身的字段定義中。
* 如果接口實現者之間對應的接口字段 解析有差異,可以在對象類型配制中指定resolveField選項(當然差異的對象類型字段不能使用$interfaceName->getField('fieldName')這種方式了),并且在那里指定句柄字段解析 (注意:字段定義的resolve選擇優先于對象類型定義中的resolveField選項)。
~~~
$NamedType = new InterfaceType([
'name' => 'Named',
'fields' => [
'name' => [
'type' => Type::string(),
'resolve' => function($v){
return 'named';
}
],
]
]);
$DogType = new ObjectType([
'name' => 'Dog',
'interfaces' => [$NamedType],
'fields' => [
$NamedType->getField('name')
],
]);
$CatType = new ObjectType([
'name' => 'Cat',
'interfaces' => [$NamedType],
'fields' => [
'name'=>Type::string(),
],
'resolveField' => function($v, $a, $c, $info){
if ($info->fieldName == 'name') {
return 'Cat';
} else {
return $v;
}
}
]);
~~~
上面的類型中,如果查詢包含DogType 及 CatType,則DogType的name字段將返回 "named",CatType 則會返回 "Cat"
#### 獲取數據中接口的角色
在數據獲取過程中接口的唯一責任是在 resolveType 中返回給定 value 的具體對象類型。然后字段的解析會委托給這個具體對象類型的解析器。
如果接口的 resolveType 配制省略,程序會循環所有接口實現者,并使用它們的 isTypeOf 回調函數去匹配第一個適合的對象類型(回調函數是否返回true),很顯然這樣做的效率低于接口單個配制項resolveType調用,因此建議盡可能定義接口的 resolveType 配制項。