wwwision/types-graphql

1.3.0 2024-02-29 16:51 UTC

This package is auto-updated.

Last update: 2024-08-29 17:50:24 UTC


README

wwwision/types包提供集成,允许从PHP代码生成GraphQL模式

用法

此包可以通过composer安装

composer require wwwision/types-graphql

要生成GraphQL模式,创建一个至少包含一个具有Query属性的公共方法的类

final class SomeApi {

    #[Query]
    public function ping(string $input): string
    {
        return strtoupper($input);
    }
}

现在,可以使用此API创建GraphQL模式

// ...
$generator = new GraphQLGenerator();
$schema = $generator->generate(SomeApi::class)->render();

assert($schema === 'type Query {
  ping(input: String!): String!
}
');

高级类型

如果API类在方法参数或返回类型中引用了更复杂的类型(使用wwwision/types包中的属性),则它们也将添加到模式中。

示例:高级类型
  1. 假设您定义了以下类
#[StringBased]
final class GivenName {
    private function __construct(public readonly string $value) {}
}

#[StringBased]
final class FamilyName {
    private function __construct(public readonly string $value) {}
}

final class FullName {
    public function __construct(
        public readonly GivenName $givenName,
        public readonly FamilyName $familyName,
    ) {}
}

#[Description('honorific title of a person')]
enum HonorificTitle
{
    #[Description('for men, regardless of marital status, who do not have another professional or academic title')]
    case MR;
    #[Description('for married women who do not have another professional or academic title')]
    case MRS;
    #[Description('for girls, unmarried women and married women who continue to use their maiden name')]
    case MISS;
    #[Description('for women, regardless of marital status or when marital status is unknown')]
    case MS;
    #[Description('for any other title that does not match the above')]
    case OTHER;
}

#[Description('A contact in the system')]
final class Contact {
    public function __construct(
        public readonly HonorificTitle $title,
        public readonly FullName $name,
        #[Description('Whether the contact is registered or not')]
        public bool $isRegistered = false,
    ) {}
}

#[ListBased(itemClassName: Contact::class)]
final class Contacts {
    private function __construct(private readonly array $contacts) {}
}
  1. ...以及这个API类
// ...
final class SomeApi {

    #[Query]
    public function findContactsByFamilyName(FamilyName $familyName): Contacts
    {
        // ...
    }

    #[Mutation]
    public function addContact(Contact $newContact): bool
    {
        // ...
    }

}
  1. 现在GraphQL模式更详细了
// ...
$generator = new GraphQLGenerator();
$schema = $generator->generate(SomeApi::class)->render();

$expectedSchema = <<<GRAPHQL
type Query {
  findContactsByFamilyName(familyName: FamilyName!): [Contact!]!
}

type Mutation {
  addContact(newContact: ContactInput!): Boolean!
}

scalar FamilyName

"""
honorific title of a person
"""
enum HonorificTitle {
  """
  for men, regardless of marital status, who do not have another professional or academic title
  """
  MR
  """
  for married women who do not have another professional or academic title
  """
  MRS
  """
  for girls, unmarried women and married women who continue to use their maiden name
  """
  MISS
  """
  for women, regardless of marital status or when marital status is unknown
  """
  MS
  """
  for any other title that does not match the above
  """
  OTHER
}

scalar GivenName

type FullName {
  givenName: GivenName!
  familyName: FamilyName!
}

type Contact {
  """ honorific title of a person """
  title: HonorificTitle!
  name: FullName!
  """ Whether the contact is registered or not """
  isRegistered: Boolean
}

input FullNameInput {
  givenName: GivenName!
  familyName: FamilyName!
}

input ContactInput {
  """ honorific title of a person """
  title: HonorificTitle!
  name: FullNameInput!
  """ Whether the contact is registered or not """
  isRegistered: Boolean
}

GRAPHQL;

assert($schema === $expectedSchema);

类型约束

GraphQL模式没有高级类型约束的概念。但GraphQLGenerator可以将它们转换为消费者可以解释的graphql-constraint指令。它还将约束规则添加到相应字段的描述中。

示例:类型约束
#[StringBased(minLength: 1, maxLength: 200)]
final class Name {
    private function __construct(public readonly string $value) {}
}

#[IntegerBased(minimum: 1, maximum: 130)]
final class Age {
    private function __construct(public readonly int $value) {}
}

#[ListBased(itemClassName: Name::class, minCount: 1, maxCount: 5)]
final class Names {
    private function __construct(private readonly array $names) {}
}

final class SomeApi {
    #[Query]
    public function oldestPerson(Names $someNames): ?Age
    {
        // ...
    }
}

$generator = new GraphQLGenerator();
$schema = $generator->generate(SomeApi::class)->render();

$expectedSchema = <<<GRAPHQL
"""
Custom constraint directive (see https://npmjs.net.cn/package/graphql-constraint-directive)
"""
directive @constraint(minLength: Int maxLength: Int pattern: String format: String min: Int max: Int minItems: Int maxItems: Int) on FIELD_DEFINITION | SCALAR | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION


type Query {
  oldestPerson(someNames: [Name!]! @constraint(minItems: 1 maxItems: 5)): Age
}

"""

*Constraints:*
* Minimum length: `1`
* Maximum length: `200`
"""
scalar Name @constraint(minLength: 1 maxLength: 200)

"""

*Constraints:*
* Minimum value: `1`
* Maximum value: `130`
"""
scalar Age @constraint(min: 1 max: 130)

GRAPHQL;

assert($schema === $expectedSchema);

自定义解析器

从版本1.2.0开始,可以注册自定义函数以动态扩展类型的行为。

注意自定义解析器闭包的签名必须包含扩展的类型作为第一个参数,并指定返回类型,例如:new CustomResolver('SomeObject', 'someCustomField', fn (SomeObject $thisIsRequired, string $thisIsOptional): bool => true)

示例:自定义解析器
final class User {
    public function __construct(
        public readonly string $givenName,
        public readonly string $familyName,
    ) {}
}

#[ListBased(itemClassName: User::class)]
final class Users {
}

final class SomeApi {
    #[Query]
    public function users(): ?Users
    {
        // ...
    }
}

$generator = new GraphQLGenerator();
$customResolvers = CustomResolvers::create(new CustomResolver('User', 'fullName', fn (User $user): string => $user->givenName . ' ' . $user->familyName));
$schema = $generator->generate(SomeApi::class, $customResolvers)->render();

$expectedSchema = <<<GRAPHQL
type Query {
  users: [User!]
}

type User {
  givenName: String!
  familyName: String!
  fullName: String!
}

GRAPHQL;

assert($schema === $expectedSchema);

贡献

问题拉取请求形式提供的贡献将受到高度赞赏

许可

查看LICENSE