
# 定义接口类型

接口是一种抽象类型，它包含一类必须包含的用于实现接口的一组字段。

在 **graphql-php** 接口类型中是一个  `GraphQL\Type\Definition\InterfaceType` 
（或其中一个子类）的实例，它接受构造函数中的配置数组：

```php
<?php
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();
        }
    }
]);
```
本示例使用 **内联** 样式来定义接口，但你也可以使用 [继承或类型语言](${doc_url}/type-system-introduction#type-definition-styles)。


# 配置选项

InterfaceType 的构造函数接受一个数组。以下是允许的选项的完整列表：

选项 | 类型 | 说明
------ | ---- | -----
name | `string` | **必需。** Schema 中该接口类型的唯一名称
fields | `array` | **必需。** 接口实现者需要定义的字段列表。与 [对象类型的字段](${doc_url}/type-system-object-types#field-configuration-options) 相同
description | `string` | 用于客户端的此类型的明文描述（例如，[GraphiQL](https://github.com/graphql/graphiql) 用于自动生成的文档）
resolveType | `callback` | **function($value, $context, [ResolveInfo](${doc_url}/class-reference/#graphql-type-definition-resolveinfo) $info)**<br> 从父字段的解析器接收 **$value**，并返回 **$value** 的具体接口实现者。


# 实现 interface

要实现该接口，只需将其添加到对象类型定义的 **接口** 数组中即可：

```php
<?php
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;

$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返回类型](#covariant-return-types-for-interface-fields) ）。

# 接口字段的协变返回类型

实现接口的对象类型可能会将字段类型更改得更具体。

例如：

```
interface A {
  field1: A
}

type B implements A {
  field1: B
}
```


# 共享接口字段
因为每个实现了接口的对象类型必须都具有相同的字段集合，所以复用对象类型中的接口字段定义合情合理。

```php
<?php
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ObjectType;

$humanType = new ObjectType([
    'name' => 'Human',
    'interfaces' => [
        $character
    ],
    'fields' => [
        'height' => Type::float(),
        $character->getField('id'),
        $character->getField('name')
    ] 
]);
```


在这个例子中，字段定义只被创建了一次（作为一个接口类型），接下来就可以被所有的接口实现者复用。它可以节省几微秒和数千字节，同时确保接口和实现者的字段定义始终一致。

但是这也带来了字段解析问题。有两种方案可以解决这个问题：

1. 如果字段解析算法在所有的接口实现者中都是一样的 - 可以简单添加
**resolve** 选项到接口本身的字段定义中。

2. 如果字段解析在各个实现中各不相同- 可以在 [对象类型配置](${doc_url}/type-system-object-types#configuration-options) 中定义 **resolveField** 选项，然后在那里处理字段解析（注意：**resolve** 选项在对象类型定义中的优先级高于 **resolveField** 选项）。

<a name="interface-role-in-data-fetching"></a>
# 数据获取中的接口角色
数据获取过程中的接口唯一职责就是返回 **resolveType** 中给定的 **$value** 的具体对象类型。然后将字段解析委派给此对象类型的解析器来处理。

如果 **resolveType** 选项被省略，graphql-php 将遍历所有的接口实现者然后使用它们的 **isTypeOf** 回调方法来选择第一个最适合的。 因此这明显比单独调用 **resolveType** 低效。所以建议尽量定义 **resolveType** 。