-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
549 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# GraphQL Attribute Schema | ||
Build your GraphQL Schema (for [webonyx/graphql-php](https://github.com/webonyx/graphql-php)) based on attributes. | ||
|
||
**Note:** this library is still work in progress, and misses some valuable features (see [todo](docs/todo.md)) | ||
|
||
## Why this library? | ||
[webonyx/graphql-php](https://github.com/webonyx/graphql-php) requires a `Schema` in order to create a GraphQL Server. | ||
This schema configuration is based on (large) PHP arrays. | ||
|
||
Wouldn't it be nice to have a library in between which can read your mutation, query and type classes instead, and create | ||
that schema configuration for you? | ||
|
||
This is where *GraphQL Attribute Schema* comes into place. By adding attributes to your classes, | ||
*GraphQL Attribute Schema* will create the schema configuration for you. | ||
|
||
## Documentation | ||
Documentation is available in the [docs](docs/index.md) directory. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
# Getting started | ||
|
||
## Requirements | ||
*GraphQL Attribute Schema* is a standalone library. However, it needs a couple of other libraries to work: | ||
|
||
- The main GraphQL library [webonyx/graphql-php](https://github.com/webonyx/graphql-php) | ||
- A [psr/container](https://github.com/php-fig/container) compatible container (PSR-11), e.g.: | ||
- [php-di/php-di](https://github.com/PHP-DI/PHP-DI) | ||
- [symfony/dependency-injection](https://github.com/symfony/dependency-injection), by default included in the Symfony framework | ||
|
||
## Installation | ||
Install via composer: | ||
```bash | ||
composer require jerowork/graphql-attribute-schema | ||
``` | ||
|
||
## Integration with webonyx/graphql-php | ||
In order to create a `Schema` for webonyx/graphql-php, a `SchemaBuilder` is provided in this library. | ||
This `SchemaBuilder` requires an 'Abstract Syntax Tree' (or AST), which can be created with the `Parser`. | ||
|
||
Both `SchemaBuilder` and `Parser` can be created quickly with the provided factories: | ||
|
||
```php | ||
use GraphQL\Server\StandardServer; | ||
use GraphQL\Server\ServerConfig; | ||
use Jerowork\GraphqlAttributeSchema\Parser\ParserFactory; | ||
use Jerowork\GraphqlAttributeSchema\SchemaBuilderFactory; | ||
|
||
// PSR-11 compatible container of your choice | ||
$container = new YourPsr11Container(); | ||
|
||
// 1. Create an AST based on your classes | ||
$ast = ParserFactory::create()->parse(__DIR__ . '/Path/To/GraphQL'); | ||
|
||
// 2. Create the schema configuration | ||
$schema = SchemaBuilderFactory::create($container)->build($ast); | ||
|
||
// 3. Add schema to e.g. webonyx StandardServer | ||
$server = new StandardServer(ServerConfig::create([ | ||
'schema' => $schema, | ||
])); | ||
``` | ||
|
||
*GraphQL Attribute Schema* does not create a GraphQL Server for you. | ||
How to create a GraphQL Server, please check e.g. https://webonyx.github.io/graphql-php/executing-queries/#using-server | ||
|
||
### Example GraphQL Server with Symfony | ||
|
||
As a quick-start, a simple example of a GraphQL Server with Symfony | ||
(requiring [symfony/psr-http-message-bridge](https://github.com/symfony/psr-http-message-bridge) and a PSR-7 implementation, e.g. [guzzlehttp/psr7](https://github.com/guzzle/psr7)): | ||
|
||
_Note: any error handling is absent to keep the example simple._ | ||
```php | ||
use GraphQL\Server\StandardServer; | ||
use GraphQL\Server\ServerConfig; | ||
use Jerowork\GraphqlAttributeSchema\Parser\ParserFactory; | ||
use Jerowork\GraphqlAttributeSchema\SchemaBuilderFactory; | ||
use Psr\Container\ContainerInterface; | ||
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\Response; | ||
use Symfony\Component\HttpKernel\Attribute\AsController; | ||
use Symfony\Component\Routing\Attribute\Route; | ||
|
||
#[AsController] | ||
final readonly class GraphQLServerController | ||
{ | ||
public function __construct( | ||
private ContainerInterface $container, | ||
private HttpMessageFactoryInterface $httpMessageFactory, | ||
) {} | ||
|
||
#[Route('/graphql', name: 'graphql.server', methods: Request::METHOD_POST)] | ||
public function __invoke(Request $request): Response | ||
{ | ||
$ast = ParserFactory::create()->parse(__DIR__ . '/Path/To/GraphQL'); | ||
$schema = SchemaBuilderFactory::create($container)->build($ast); | ||
|
||
$server = new StandardServer(ServerConfig::create([ | ||
'schema' => $schema, | ||
])); | ||
|
||
$result = $server->executePsrRequest( | ||
$this->httpMessageFactory | ||
->createRequest($request) | ||
->withParsedBody(json_decode($request->getContent(), true, flags: JSON_THROW_ON_ERROR)), | ||
); | ||
|
||
// Batch requests | ||
if (is_array($result)) { | ||
return new JsonResponse(array_map(fn($result) => $result->toArray(), $result)); | ||
} | ||
|
||
// Single request | ||
return new JsonResponse($result->toArray()); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# GraphQL Attribute Schema | ||
Build your GraphQL Schema (for [webonyx/graphql-php](https://github.com/webonyx/graphql-php)) based on attributes. | ||
|
||
**Note:** this library is still work in progress, and misses some valuable features (see [todo](todo.md)) | ||
|
||
## Why this library? | ||
[webonyx/graphql-php](https://github.com/webonyx/graphql-php) requires a `Schema` in order to create a GraphQL Server. | ||
This schema configuration is based on (large) PHP arrays. | ||
|
||
Wouldn't it be nice to have a library in between which can read your mutation, query and type classes instead, and create | ||
that schema configuration for you? | ||
|
||
This is where *GraphQL Attribute Schema* comes into place. By adding attributes to your classes, | ||
*GraphQL Attribute Schema* will create the schema configuration for you. | ||
|
||
## Documentation | ||
- [Getting started](getting_started.md) | ||
- [Usage](usage.md) | ||
|
||
## A simple example | ||
```php | ||
use Jerowork\GraphqlAttributeSchema\Attribute\Enum; | ||
use Jerowork\GraphqlAttributeSchema\Attribute\Field; | ||
use Jerowork\GraphqlAttributeSchema\Attribute\InputType; | ||
use Jerowork\GraphqlAttributeSchema\Attribute\Mutation; | ||
use Jerowork\GraphqlAttributeSchema\Attribute\Type; | ||
|
||
#[Mutation] | ||
final readonly class CreateUserMutation | ||
{ | ||
public function __invoke(CreateUserInputType $input): User | ||
{ | ||
// Do your magic; create your user here and return | ||
} | ||
} | ||
|
||
#[Query(description: 'Get a user')] | ||
final readonly class UserQuery | ||
{ | ||
public function __invoke(int $userid): User | ||
{ | ||
// Do your magic; retrieve your user and return | ||
} | ||
} | ||
|
||
#[InputType] | ||
final readonly class CreateUserInputType | ||
{ | ||
public function __construct( | ||
#[Field] | ||
public int $userId, | ||
#[Field] | ||
public string $name, | ||
#[Field(name: 'phoneNumber')] | ||
public ?string $phone, | ||
) {} | ||
} | ||
|
||
#[Type] | ||
final readonly class User | ||
{ | ||
// Define fields by property | ||
public function __construct( | ||
#[Field] | ||
public int $userId, | ||
#[Field] | ||
public string $name, | ||
public ?string $phone, | ||
#[Field(description: 'The status of the user')] | ||
public UserStatusType $status, | ||
) {} | ||
|
||
// Or define fields by method for additional logic | ||
#[Field] | ||
public function getPhoneNumber(): string | ||
{ | ||
return sprintf('+31%s', $this->phone); | ||
} | ||
} | ||
|
||
#[Enum(description: 'The status of the user')] | ||
enum UserStatusType: string | ||
{ | ||
case Created = 'CREATED'; | ||
case Removed = 'REMOVED'; | ||
} | ||
``` | ||
|
||
Will result in the following GraphQL schema: | ||
```graphql | ||
type Mutation { | ||
createUser(input: CreateUserInput!): User! | ||
} | ||
|
||
type Query { | ||
user(userId: Int!): User! | ||
} | ||
|
||
type CreateUserInput { | ||
userId: Int! | ||
name: String! | ||
phoneNumber: string | ||
} | ||
|
||
type User { | ||
userId: Int! | ||
name: String! | ||
status: UserStatus! | ||
phoneNumber: string | ||
} | ||
|
||
enum UserStatus { | ||
CREATED | ||
REMOVED | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
# Todo | ||
This library is still work in progress, and misses some valuable features: | ||
|
||
- Introduction of `#[Deprecated]` attribute | ||
- Overwrite type via attributes | ||
- Allow simple lists (array type) | ||
- Connection, edge, nodes (see https://relay.dev/graphql/connections.htm) | ||
- Make AST serializable (cacheable) | ||
- Handle `DateTime` and `DateTimeImmutable` | ||
- GraphQL interfaces, inheritance | ||
- Inject autowiring services | ||
- Subscriptions |
Oops, something went wrong.