<?php
/**
 * 案例052:GraphQL接入
 * 说明:用hyperf/graphql实现GraphQL查询,支持类型系统和复杂查询
 * 需要安装的包:hyperf/graphql (^3.1),底层用webonyx/graphql-php
 */

declare(strict_types=1);

namespace App\GraphQL;

use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Schema;
use GraphQL\GraphQL;
use GraphQL\Error\DebugFlag;

/**
 * 定义User类型
 * GraphQL的类型系统是强类型的,每个字段都要定义类型
 */
class UserType extends ObjectType
{
    public function __construct()
    {
        parent::__construct([
            'name'        => 'User',
            'description' => '用户信息',
            'fields'      => fn() => [ // 用闭包延迟加载,避免循环依赖
                'id'       => ['type' => Type::int(), 'description' => '用户ID'],
                'name'     => ['type' => Type::string(), 'description' => '用户名'],
                'email'    => ['type' => Type::string()],
                'age'      => ['type' => Type::int()],
                // 关联字段:查用户时可以顺带查他的订单(解决REST接口多次请求问题)
                'orders'   => [
                    'type'    => Type::listOf(TypeRegistry::order()), // User -> [Order]
                    'resolve' => function ($user) {
                        // 只有前端查了orders字段才会执行这里,否则不查DB
                        return \Hyperf\DbConnection\Db::table('orders')
                            ->where('user_id', $user['id'])
                            ->get()
                            ->toArray();
                    },
                ],
            ],
        ]);
    }
}

/**
 * 定义Order类型
 */
class OrderType extends ObjectType
{
    public function __construct()
    {
        parent::__construct([
            'name'   => 'Order',
            'fields' => [
                'id'         => ['type' => Type::int()],
                'amount'     => ['type' => Type::float()],
                'status'     => ['type' => Type::string()],
                'created_at' => ['type' => Type::string()],
            ],
        ]);
    }
}

/**
 * 类型注册表,避免重复创建类型实例(GraphQL类型必须是单例)
 */
class TypeRegistry
{
    private static array $types = [];

    public static function user(): UserType
    {
        return self::$types['User'] ??= new UserType();
    }

    public static function order(): OrderType
    {
        return self::$types['Order'] ??= new OrderType();
    }
}

/**
 * Query根类型(读操作)
 */
class QueryType extends ObjectType
{
    public function __construct()
    {
        parent::__construct([
            'name'   => 'Query',
            'fields' => [
                // 查单个用户:query { user(id: 1) { name, email } }
                'user' => [
                    'type'    => TypeRegistry::user(),
                    'args'    => ['id' => Type::nonNull(Type::int())], // id是必填参数
                    'resolve' => function ($root, array $args) {
                        $user = \Hyperf\DbConnection\Db::table('users')->find($args['id']);
                        return $user ? (array) $user : null;
                    },
                ],
                // 用户列表:query { users(limit: 10) { id, name } }
                'users' => [
                    'type'    => Type::listOf(TypeRegistry::user()),
                    'args'    => [
                        'limit'   => ['type' => Type::int(), 'defaultValue' => 20],
                        'keyword' => ['type' => Type::string()],
                    ],
                    'resolve' => function ($root, array $args) {
                        $query = \Hyperf\DbConnection\Db::table('users');
                        if (!empty($args['keyword'])) {
                            $query->where('name', 'like', '%' . $args['keyword'] . '%');
                        }
                        return $query->limit($args['limit'])->get()->toArray();
                    },
                ],
            ],
        ]);
    }
}

/**
 * Mutation根类型(写操作)
 */
class MutationType extends ObjectType
{
    public function __construct()
    {
        parent::__construct([
            'name'   => 'Mutation',
            'fields' => [
                // 创建用户:mutation { createUser(name: "张三", email: "test@test.com") { id, name } }
                'createUser' => [
                    'type' => TypeRegistry::user(),
                    'args' => [
                        'name'  => Type::nonNull(Type::string()),
                        'email' => Type::nonNull(Type::string()),
                        'age'   => Type::int(),
                    ],
                    'resolve' => function ($root, array $args) {
                        $id = \Hyperf\DbConnection\Db::table('users')->insertGetId([
                            'name'       => $args['name'],
                            'email'      => $args['email'],
                            'age'        => $args['age'] ?? null,
                            'created_at' => date('Y-m-d H:i:s'),
                        ]);
                        return \Hyperf\DbConnection\Db::table('users')->find($id);
                    },
                ],
            ],
        ]);
    }
}

/**
 * GraphQL HTTP Controller
 * 所有GraphQL请求都走这一个接口 POST /graphql
 */
class GraphQLController
{
    public function handle(\Hyperf\HttpServer\Contract\RequestInterface $request): array
    {
        // 创建Schema,把Query和Mutation注册进去
        $schema = new Schema([
            'query'    => new QueryType(),
            'mutation' => new MutationType(),
        ]);

        $body      = $request->post();
        $query     = $body['query'] ?? '';        // GraphQL查询字符串
        $variables = $body['variables'] ?? null;  // 变量(前端传的参数)

        // 执行GraphQL查询
        $result = GraphQL::executeQuery($schema, $query, null, null, $variables);

        // 开发环境输出详细错误,生产环境不要暴露错误详情
        $debug  = env('APP_ENV') === 'dev' ? DebugFlag::INCLUDE_DEBUG_MESSAGE : DebugFlag::NONE;
        return $result->toArray($debug);
    }
}
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐