monkeyscloud/monkeyslegion-graphql
Composer 安装命令:
composer require monkeyscloud/monkeyslegion-graphql
包简介
Code-first GraphQL server for the MonkeysLegion framework — PHP 8.4 attributes, PSR-15, DataLoader, subscriptions, and security out of the box.
README 文档
README
Code-first GraphQL server for the MonkeysLegion framework — PHP 8.4 attributes, PSR-15, DataLoader, subscriptions, and security out of the box.
Requirements
- PHP 8.4+
webonyx/graphql-php^15.30
Installation
composer require monkeyscloud/monkeyslegion-graphql
The GraphQLProvider is auto-registered via composer.json extra.
Quick Start
1. Define a Type
use MonkeysLegion\GraphQL\Attribute\{Type, Field}; #[Type(description: 'A user')] final class UserType { #[Field] public function id(User $root): int { return $root->id; } #[Field] public function name(User $root): string { return $root->name; } #[Field(description: 'Email address')] public function email(User $root): string { return $root->email; } }
2. Define a Query
use MonkeysLegion\GraphQL\Attribute\{Query, Arg}; use MonkeysLegion\GraphQL\Context\GraphQLContext; #[Query(name: 'user', description: 'Get user by ID')] final class GetUserQuery { public function __construct(private UserRepository $users) {} public function __invoke( mixed $root, #[Arg(description: 'User ID')] int $id, GraphQLContext $context, ): ?User { return $this->users->find($id); } }
3. Define a Mutation
use MonkeysLegion\GraphQL\Attribute\{Mutation, Arg}; #[Mutation(name: 'createUser', description: 'Create a new user')] final class CreateUserMutation { public function __construct(private UserRepository $users) {} public function __invoke( mixed $root, #[Arg] string $name, #[Arg] string $email, ): User { return $this->users->create($name, $email); } }
4. Configure
# config/graphql.mlc graphql: endpoint: /graphql scan: directories: - app/GraphQL security: max_depth: 10 max_complexity: 200
Features
Attributes
| Attribute | Target | Purpose |
|---|---|---|
#[Type] |
Class | GraphQL object type |
#[Field] |
Method/Property | Object type field |
#[Query] |
Class | Root query field |
#[Mutation] |
Class | Root mutation field |
#[Subscription] |
Class | Subscription field |
#[Arg] |
Parameter | Argument metadata |
#[InputType] |
Class | Input object type |
#[Enum] |
Backed enum | Enum type |
#[InterfaceType] |
Class/Interface | Interface type |
#[UnionType] |
Class | Union type |
#[Middleware] |
Class/Method | Per-field middleware |
Custom Scalars
DateTime— ISO 8601 serializationJSON— Arbitrary JSON passthroughEmail— Email format validationURL— URL format validationUpload— Multipart file upload
Security
graphql: security: max_depth: 10 # Query depth limiting max_complexity: 200 # Field cost analysis introspection: false # Disable introspection in production persisted_queries: true # APQ with SHA256 rate_limit: enabled: true max_requests: 100 window_seconds: 60
DataLoader (N+1 Prevention)
use MonkeysLegion\GraphQL\Loader\DataLoader; final class UserLoader extends DataLoader { public function __construct(private UserRepository $users) {} protected function batchLoad(array $keys): array { $users = $this->users->findByIds($keys); return array_map( fn(int $id) => $users[$id] ?? null, $keys, ); } }
Relay Pagination
use MonkeysLegion\GraphQL\Type\ConnectionType; // Automatically creates UserConnection, UserEdge, PageInfo types $connectionType = ConnectionType::create('User', $userType);
Subscriptions
use MonkeysLegion\GraphQL\Attribute\Subscription; #[Subscription(name: 'messageAdded', description: 'New message')] final class MessageAddedSubscription { public function __invoke(mixed $root): Message { return $root; } }
Supports graphql-ws protocol with in-memory and Redis PubSub backends.
File Uploads
Follows the GraphQL multipart request spec:
#[Mutation(name: 'uploadFile')] final class UploadFileMutation { public function __invoke( mixed $root, #[Arg] UploadedFileInterface $file, ): string { $file->moveTo('/uploads/' . $file->getClientFilename()); return $file->getClientFilename(); } }
CLI Commands
| Command | Description |
|---|---|
php ml graphql:schema:dump |
Dump schema as SDL |
php ml graphql:schema:validate |
Validate schema |
php ml graphql:cache:warm |
Warm schema cache |
php ml graphql:cache:clear |
Clear schema cache |
php ml graphql:introspect |
Dump introspection JSON |
Entity Integration
When monkeyslegion-entity is installed, you can auto-map your entities to GraphQL types without writing boilerplate type classes.
Entity Example
use MonkeysLegion\Entity\Attribute\Entity; use MonkeysLegion\Entity\Attribute\Id; use MonkeysLegion\Entity\Attribute\Column; #[Entity(table: 'products')] class Product { #[Id] public int $id; #[Column(type: 'varchar', length: 255)] public string $name; #[Column(type: 'text')] public string $description; #[Column(type: 'decimal')] public float $price; #[Column(type: 'boolean')] public bool $active; #[Column(type: 'datetime')] public \DateTimeImmutable $createdAt; }
Auto-Map Entities to GraphQL Types
use MonkeysLegion\GraphQL\Scanner\EntityTypeMapper; $mapper = new EntityTypeMapper(); // Maps all typed properties → GraphQL fields automatically: // int → Int! float → Float! // string → String! bool → Boolean! // DateTime* → DateTime! (custom scalar) // ?string → String (nullable) $typeConfig = $mapper->map(Product::class); // Returns: ['name' => 'Product', 'fields' => ['id' => ..., 'name' => ..., ...]] // Map multiple entities at once $types = $mapper->mapAll([Product::class, Category::class, Order::class]);
Auto-Generated CRUD Resolvers
Use EntityResolver to expose entities without manual resolver classes:
use MonkeysLegion\GraphQL\Resolver\EntityResolver; use MonkeysLegion\GraphQL\Type\ConnectionType; // Single entity by ID // query { product(id: 42) { name price } } $findProduct = EntityResolver::findById( Product::class, ProductRepository::class, // optional — defaults to Product::class . 'Repository' ); // List all entities // query { products { name price active } } $listProducts = EntityResolver::findAll(Product::class); // Relay-style pagination with cursors // query { products(first: 10, after: "Y3Vyc29yOjk=") { // edges { node { name } cursor } // pageInfo { hasNextPage endCursor } // totalCount // }} $paginatedProducts = EntityResolver::connection(Product::class);
Full Example: Entity-Backed Schema
use MonkeysLegion\GraphQL\Attribute\{Type, Field, Query, Arg}; use MonkeysLegion\GraphQL\Context\GraphQLContext; // 1. Define the GraphQL type wrapping the entity #[Type(description: 'A product in the catalog')] final class ProductType { #[Field] public function id(Product $root): int { return $root->id; } #[Field] public function name(Product $root): string { return $root->name; } #[Field] public function price(Product $root): float { return $root->price; } #[Field(description: 'Active in store?')] public function active(Product $root): bool { return $root->active; } #[Field(description: 'ISO 8601')] public function createdAt(Product $root): string { return $root->createdAt->format('c'); } } // 2. Query resolver using the repository from DI #[Query(name: 'product', description: 'Find product by ID')] final class GetProductQuery { public function __construct(private ProductRepository $products) {} public function __invoke( mixed $root, #[Arg(description: 'Product ID')] int $id, GraphQLContext $context, ): ?Product { return $this->products->find($id); } } // 3. List with filtering #[Query(name: 'products', description: 'List products')] final class ListProductsQuery { public function __construct(private ProductRepository $products) {} public function __invoke( mixed $root, #[Arg(nullable: true)] ?bool $active, #[Arg(nullable: true, defaultValue: 20)] int $limit, ): array { if ($active !== null) { return $this->products->findByActive($active, $limit); } return $this->products->findAll($limit); } }
Route Registration
GraphQLProvider automatically registers routes with monkeyslegion-router when the application boots.
Default Routes
| Method | Path | Handler | Description |
|---|---|---|---|
POST |
/graphql |
GraphQLMiddleware |
Queries & mutations |
GET |
/graphql |
GraphQLMiddleware |
Simple GET queries |
GET |
/graphiql |
GraphiQLMiddleware |
Interactive IDE (dev) |
Configuration
# config/graphql.mlc graphql: endpoint: /graphql # Change the endpoint path graphiql_enabled: true # Disable GraphiQL in production graphiql_endpoint: /graphiql # Custom GraphiQL path debug: false # Enable for detailed error traces scan_dirs: - app/GraphQL # Where to find Type/Query/Mutation classes scan_namespace: App\GraphQL # PSR-4 namespace for scanned classes security: max_depth: 10 max_complexity: 200 introspection: true # Disable in production persisted_queries: false rate_limit: max_requests: 100 window_seconds: 60 cache: enabled: false ttl: 3600 # Schema cache TTL in seconds subscriptions: enabled: false driver: memory # 'memory' or 'redis' host: 0.0.0.0 port: 6001 redis_dsn: redis://127.0.0.1:6379
How Route Registration Works
The GraphQLProvider::register() method is called automatically during application bootstrap (via the monkeyslegion extra in composer.json). Here's what happens:
// This happens automatically — no manual setup needed. // The provider: // 1. Reads config/graphql.mlc // 2. Registers all GraphQL services in the DI container // 3. Registers routes with MonkeysLegion\Router\Router // Routes are registered as closures that delegate to PSR-15 middleware: $router->post('/graphql', $graphqlHandler, 'graphql'); $router->get('/graphql', $graphqlHandler, 'graphql.get'); $router->get('/graphiql', $graphiqlHandler, 'graphiql'); // if enabled
Custom Route Middleware
Stack your own middleware (auth, CORS, rate-limiting) alongside GraphQL:
use MonkeysLegion\GraphQL\Attribute\Middleware; // Per-resolver middleware #[Middleware('App\Middleware\AuthMiddleware')] #[Middleware('App\Middleware\RateLimitMiddleware')] #[Query(name: 'adminUsers')] final class AdminUsersQuery { public function __invoke(mixed $root, GraphQLContext $context): array { // Only reached if auth + rate-limit pass return $context->container->get(UserRepository::class)->findAdmins(); } }
Testing the Endpoint
# Simple query curl -X POST http://localhost:8080/graphql \ -H 'Content-Type: application/json' \ -d '{"query": "{ product(id: 1) { name price } }"}' # Mutation curl -X POST http://localhost:8080/graphql \ -H 'Content-Type: application/json' \ -d '{"query": "mutation { createProduct(name: \"Widget\", price: 9.99) { id name } }"}' # GET request (simple queries only) curl 'http://localhost:8080/graphql?query=\{products\{name\}\}' # Open GraphiQL IDE in browser open http://localhost:8080/graphiql
Facade
use MonkeysLegion\GraphQL\GraphQL; // Execute a query programmatically $result = GraphQL::execute('{ user(id: 1) { name } }'); // Publish a subscription event GraphQL::publish('messageAdded', $message); // Get the built schema $schema = GraphQL::schema();
License
MIT — see LICENSE for details.
monkeyscloud/monkeyslegion-graphql 适用场景与选型建议
monkeyscloud/monkeyslegion-graphql 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 0 次下载、GitHub Stars 达 1, 最近一次更新时间为 2025 年 06 月 18 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「php」 「attributes」 「subscriptions」 「graphql」 「psr-15」 「dataLoader」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 monkeyscloud/monkeyslegion-graphql 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 monkeyscloud/monkeyslegion-graphql 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 monkeyscloud/monkeyslegion-graphql 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
A library that allows you to easily use the PHP-VCR library in your PHPUnit tests.
This Package provides some usefully console features like the attribute syntax for arguments and options, validation, auto ask and casting.
Handling Appstore server to server notifications
The last validation library you will ever need!
A PHPDoc docblock interpreter that is simple, easy to use, and provides Attribute alternatives.
Module with SP files for attribute_check
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 1
- 点击次数: 20
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-06-18