liquiddesign / lqgraphi
为 Liquid Design 生态系统提供的 GraphQL API 库
Requires
- php: >=8.2
- ext-intl: *
- ext-json: *
- ext-pdo: *
- contributte/psr7-http-message: ^0.10.0
- haydenpierce/class-finder: ^0.5.3
- liquiddesign/base: ^2.0
- liquiddesign/common: ^2.0
- liquiddesign/storm: ^2.0
- mll-lab/graphql-php-scalars: ^6.3
- simpod/graphql-utils: ^0.7
- webonyx/graphql-php: ^15.13
Requires (Dev)
- dev-main
- v2.0.1
- v2.0.0
- v1.0.1
- v1.0.0
- v1.0.0-beta.2
- v1.0.0-beta.1
- v0.3.0
- v0.2.1
- v0.2.0
- v0.1.6
- v0.1.5
- v0.1.4
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
- v0.0.30
- v0.0.29
- v0.0.28
- v0.0.27
- v0.0.26
- v0.0.25
- v0.0.24
- v0.0.23
- v0.0.22
- v0.0.21
- v0.0.20
- v0.0.19
- v0.0.18
- v0.0.17
- v0.0.16
- v0.0.15
- v0.0.14
- v0.0.13
- v0.0.12
- v0.0.11
- v0.0.10-alpha
- v0.0.9-alpha
- v0.0.8-alpha
- v0.0.7-alpha
- v0.0.6-alpha
- v0.0.5-alpha
- v0.0.4-alpha
- v0.0.3-alpha
- v0.0.2-alpha
- v0.0.1-alpha
This package is auto-updated.
Last update: 2024-09-06 12:45:14 UTC
README
GraphQL API 库,用于 Liquid Design 生态系统。
功能
- 从 PHP 类自动创建到 TypeRegister(Storm 实体)的类型
- 从命名空间自动加载查询、变异和类型
- 缓存模式、持久查询和变异
- 通用的 CRUD 查询、变异和解析器,用于通用生成和解析
- 基于请求查询从数据库递归获取 Storm 实体的数据检索器(高度优化 - 每个实体类只执行一个查询 - 模拟数据加载器)
推荐
此包与扩展包配合使用效果良好,这些扩展包为 LQD 包提供了类型
安装
此包需要 PHP 8.2 或更高版本。
composer require liquiddesign/lqgraphi
配置
extensions: typeRegister: LqGrAphi\LqGrAphiDI typeRegister: resolvers: - EshopApi\Resolvers queriesAndMutations: - EshopApi\Schema\Types types: output: - EshopApi\Schema\Outputs input: - EshopApi\Schema\Inputs
入口点
在您的入口点(可能是 index.php
)中,您需要调用处理程序。您只需创建容器并将其传递给 \LqGrAphi\Handlers\IndexHandler::handle
。
index.php
的最小示例
require __DIR__ . '/vendor/autoload.php'; \LqGrAphi\Handlers\IndexHandler::handle(\EshopApi\Bootstrap::boot()->createContainer());
沙盒
默认启用 Apollo 沙盒,用于基于环境文件的调试连接。
您可以永久禁用它
require __DIR__ . '/vendor/autoload.php'; \LqGrAphi\Handlers\IndexHandler::handle(\EshopApi\Bootstrap::boot()->createContainer(), false);
查询和变异
查询和变异的位置通过配置 queryAndMutationsNamespace
设置。查询需要扩展 \LqGrAphi\Schema\BaseQuery
,变异扩展 \LqGrAphi\Schema\BaseMutation
。
这些类型仅在首次创建模式时自动加载并缓存。所有其他请求都使用缓存的模式,因此脚本不需要为每个请求创建模式,性能不会降低。这种方法有一些限制:查询和变异未在容器中注册,因此您不能使用 DI。所有这些类都将接收容器作为第一个参数。
类型
类型的位置通过配置 types
设置。您需要在此处指定所有使用的输入和输出。
typeRegister: types: input: product: EshopApi\Schema\Inputs\ProductInput output: product: EshopApi\Schema\Outputs\ProductOutput
有关更多信息,请访问 webonyx/graphql-php 库的文档。
ClassOutput
存在一个具有 getClass
方法的接口 \LqGrAphi\Schema\ClassOutput
。如果您使用它,TypeRegister 将保存此映射,当您调用 getOutputType
时,您只需传递类字符串而不是名称。
ClassInput
与 ClassOutput 相同,用于输入。
关系
在输出中,关系是对象或对象列表,深度可达 10 级。另一方面,输入始终有两个用于关系的字段。
一个带有后缀 ID
的字段用于单个关系,它接受字符串(如果可能则为 null)。对于多关系,有一个带有后缀 IDs
的字段,它是一个对象,具有 add
、remove
和 replace
字段。这些字段接受字符串列表。
其次,始终有带有后缀 OBJ
的字段,它直接接受输入对象并更新它。它也可以有 10 级深度。对于多关系,有一个带有后缀 OBJs
的字段,它接受输入对象列表。
由于 GraphQL 的限制,您无法使用联合输入类型,因此这些输入始终是 UpdateInput 变体,其中所有字段都是可选的。根据 ID 字段,对象被创建或仅更新。这种方法在创建对象时丢失了必需字段的类型安全,在这种情况下,错误仅在运行时引发。
input Object { fullName: String accountsIDs: SubObjectIDs accountsOBJs: [SubObjectUpdateInput] }
存在一个名为 \LqGrAphi\Schema\ClassInput
的接口,其中包含 getClass
方法。如果您使用它,TypeRegister 将保存此映射,当您调用 getInputType
时,您可以直接传递类字符串而不是配置中的名称。此外,TypeRegister 还将此输入映射到输入对象的关联字段。
解析器
由于整个模式创建的缓存,解析器与模式隔离,并必须自行解析请求。
存在简单的路由机制。GraphQL 的查询和突变名称使用小驼峰命名法。
名称被解析为解析器类名称中的第一个单词,其余为函数名称。
例如:productGetMany
被解析为 ProductResolver
和函数 getMany
。
解析器名称到解析器和函数的路由器使用按需缓存。
每个解析器函数的签名必须是
/** * @param array<mixed> $rootValue * @param array<mixed> $args * @param \LqGrAphi\GraphQLContext $context * @param \GraphQL\Type\Definition\ResolveInfo|array<mixed> $resolveInfo * @return array<mixed>|null * @throws \LqGrAphi\Resolvers\Exceptions\BadRequestException * @throws \ReflectionException * @throws \StORM\Exception\GeneralException */ public function getMany(array $rootValue, array $args, GraphQLContext $context, ResolveInfo|array $resolveInfo): ?array { ... }
推荐配置方式
services: graphql_resolvers: in: %appDir% files: [Resolvers/*Resolver.php, Resolvers/*/*Resolver.php] implements: - LqGrAphi\Resolvers\BaseResolver
缓存
处理程序使用缓存来记住查询和突变。如果您发送相同的查询两次,它将从缓存中解析并直接传递给解析器。这种方法显着提高了性能。从解析器返回的数据仅与第一个请求进行验证。当解析器未完全测试时,这种方法不安全。
您还可以直接传递 queryId 而不是查询。查询通过 md5 进行散列,因此您可以仅对查询进行 md5 散列并将其作为查询Id发送。但请记住,您仍然需要发送至少一个带有查询的请求来验证它。
CRUD
您可以自己编写类型和查询,但大多数时候,您只想使用现有实体并对其进行 CRUD 操作。
此时出现了 \LqGrAphi\Schema\CrudQuery
和 \LqGrAphi\Schema\CrudMutation
。
首先,创建查询类
class CustomerQuery extends CrudQuery { public function getClass(): string { return Customer::class; } }
然后创建输出、创建和更新类型,使用助手
class CustomerOutput extends BaseOutput { public function __construct(TypeRegister $typeRegister) { $config = [ 'fields' => $typeRegister->createOutputFieldsFromClass(Customer::class, exclude: ['account']), ]; parent::__construct($config); } }
class CustomerCreateInput extends BaseInput { public function __construct(TypeRegister $typeRegister) { $config = [ 'fields' => $typeRegister->createInputFieldsFromClass(Customer::class, includeId: false), ]; parent::__construct($config); } }
class CustomerUpdateInput extends BaseInput { public function __construct(TypeRegister $typeRegister) { $config = [ 'fields' => $typeRegister->createInputFieldsFromClass(Customer::class, forceAllOptional: true), ]; parent::__construct($config); } }
在配置中注册您的类型命名空间,最后创建解析器
class CustomerResolver extends CrudResolver { public function getClass(): string { return Customer::class; } }
就这样,您可以查询一个、多个或集合,并执行创建、更新和删除操作。
CRUD 辅助函数
为了帮助处理 API,有一些自动改进
- 所有数组输出都被封装在具有键 data 和 onPageCount 的对象中
- 存在用于排序、分页和过滤的通用输入对象
过滤
过滤器是类型为 JSON 的输入字段,它们被解析为存储库允许的 filter 函数。因此,过滤器是动态类型化的。
获取结果
存在通用的获取函数。您只需传递带有 ResolveInfo 的集合。它将负责以最有效的方式检索您请求的数据。
语言突变
如果您需要使用不同的语言突变,可以使用 HTTP 头部 "Accept-Language"。系统将检测到的语言(如果受支持)设置为所有 SQL 查询的主要语言。如果 "Accept-Language" 头部中没有受支持的语言,则使用设置中的主要语言。
路线图
2023
- ✅ 带缓存的持久查询
- ❗新的类型加载系统以提供更好的 DX
- 无需注册类型
- 从命名空间加载类型
- 重构 TypeRegister
- 安全 - 防护、登录
- 自动测试
开发者信息
项目使用 PHPStan(级别 8)和 PHP-CS-Fixer 进行代码质量检查。