Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
34
src/Framework/GraphQL/GraphQLArgument.php
Normal file
34
src/Framework/GraphQL/GraphQLArgument.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents a GraphQL argument
|
||||
*/
|
||||
final readonly class GraphQLArgument
|
||||
{
|
||||
public function __construct(
|
||||
public GraphQLFieldType $type,
|
||||
public mixed $defaultValue = null,
|
||||
public ?string $description = null
|
||||
) {
|
||||
}
|
||||
|
||||
public function toDefinition(string $name): string
|
||||
{
|
||||
$def = $name . ': ' . $this->type->toString();
|
||||
|
||||
if ($this->defaultValue !== null) {
|
||||
$def .= ' = ' . json_encode($this->defaultValue);
|
||||
}
|
||||
|
||||
return $def;
|
||||
}
|
||||
|
||||
public function hasDefaultValue(): bool
|
||||
{
|
||||
return $this->defaultValue !== null;
|
||||
}
|
||||
}
|
||||
183
src/Framework/GraphQL/GraphQLExecutor.php
Normal file
183
src/Framework/GraphQL/GraphQLExecutor.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
use App\Framework\Exception\ErrorCode;
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
|
||||
/**
|
||||
* Executes GraphQL queries against a schema
|
||||
*/
|
||||
final readonly class GraphQLExecutor
|
||||
{
|
||||
public function __construct(
|
||||
private GraphQLSchema $schema,
|
||||
private GraphQLQueryParser $parser
|
||||
) {
|
||||
}
|
||||
|
||||
public function execute(
|
||||
string $query,
|
||||
array $variables = [],
|
||||
mixed $context = null,
|
||||
mixed $rootValue = null
|
||||
): GraphQLResult {
|
||||
try {
|
||||
// Parse the query
|
||||
$parsedQuery = $this->parser->parse($query);
|
||||
|
||||
// Execute based on operation type
|
||||
$data = match ($parsedQuery->operationType) {
|
||||
GraphQLOperationType::QUERY => $this->executeQuery($parsedQuery, $variables, $context, $rootValue),
|
||||
GraphQLOperationType::MUTATION => $this->executeMutation($parsedQuery, $variables, $context, $rootValue),
|
||||
GraphQLOperationType::SUBSCRIPTION => throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
'Subscriptions are not supported yet'
|
||||
),
|
||||
};
|
||||
|
||||
return new GraphQLResult(data: $data);
|
||||
|
||||
} catch (FrameworkException $e) {
|
||||
return new GraphQLResult(
|
||||
errors: [
|
||||
[
|
||||
'message' => $e->getMessage(),
|
||||
'extensions' => [
|
||||
'code' => $e->getErrorCode()->value,
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
return new GraphQLResult(
|
||||
errors: [
|
||||
[
|
||||
'message' => 'Internal server error',
|
||||
'extensions' => [
|
||||
'code' => 'INTERNAL_ERROR',
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private function executeQuery(
|
||||
GraphQLParsedQuery $parsedQuery,
|
||||
array $variables,
|
||||
mixed $context,
|
||||
mixed $rootValue
|
||||
): array {
|
||||
$result = [];
|
||||
|
||||
foreach ($parsedQuery->fields as $field) {
|
||||
$queryField = $this->schema->getQuery($field->name);
|
||||
|
||||
if ($queryField === null) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
"Query field '{$field->name}' not found"
|
||||
);
|
||||
}
|
||||
|
||||
// Merge arguments with variables
|
||||
$args = $this->resolveArguments($field->arguments, $variables);
|
||||
|
||||
// Execute resolver
|
||||
$fieldResult = $queryField->resolve($rootValue, $args, $context);
|
||||
|
||||
// Handle sub-field selection if needed
|
||||
if ($field->hasSubFields() && is_array($fieldResult)) {
|
||||
$fieldResult = $this->selectSubFields($fieldResult, $field->subFields);
|
||||
}
|
||||
|
||||
$result[$field->name] = $fieldResult;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function executeMutation(
|
||||
GraphQLParsedQuery $parsedQuery,
|
||||
array $variables,
|
||||
mixed $context,
|
||||
mixed $rootValue
|
||||
): array {
|
||||
$result = [];
|
||||
|
||||
foreach ($parsedQuery->fields as $field) {
|
||||
$mutationField = $this->schema->getMutation($field->name);
|
||||
|
||||
if ($mutationField === null) {
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
"Mutation field '{$field->name}' not found"
|
||||
);
|
||||
}
|
||||
|
||||
// Merge arguments with variables
|
||||
$args = $this->resolveArguments($field->arguments, $variables);
|
||||
|
||||
// Execute resolver
|
||||
$fieldResult = $mutationField->resolve($rootValue, $args, $context);
|
||||
|
||||
// Handle sub-field selection if needed
|
||||
if ($field->hasSubFields() && is_array($fieldResult)) {
|
||||
$fieldResult = $this->selectSubFields($fieldResult, $field->subFields);
|
||||
}
|
||||
|
||||
$result[$field->name] = $fieldResult;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function resolveArguments(array $fieldArgs, array $variables): array
|
||||
{
|
||||
$resolved = [];
|
||||
|
||||
foreach ($fieldArgs as $key => $value) {
|
||||
// Handle variable references
|
||||
if (is_string($value) && str_starts_with($value, '$')) {
|
||||
$varName = substr($value, 1);
|
||||
$resolved[$key] = $variables[$varName] ?? null;
|
||||
} else {
|
||||
$resolved[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
private function selectSubFields(array $data, array $subFields): array
|
||||
{
|
||||
if (empty($subFields)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// Check if it's a list of items
|
||||
$isList = isset($data[0]) && is_array($data[0]);
|
||||
|
||||
if ($isList) {
|
||||
return array_map(fn ($item) => $this->filterFields($item, $subFields), $data);
|
||||
}
|
||||
|
||||
return $this->filterFields($data, $subFields);
|
||||
}
|
||||
|
||||
private function filterFields(array $item, array $fields): array
|
||||
{
|
||||
$filtered = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $item)) {
|
||||
$filtered[$field] = $item[$field];
|
||||
}
|
||||
}
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
}
|
||||
54
src/Framework/GraphQL/GraphQLField.php
Normal file
54
src/Framework/GraphQL/GraphQLField.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents a GraphQL field with type and resolver
|
||||
*/
|
||||
final readonly class GraphQLField
|
||||
{
|
||||
/**
|
||||
* @param array<string, GraphQLArgument> $arguments
|
||||
*/
|
||||
public function __construct(
|
||||
public GraphQLFieldType $type,
|
||||
public \Closure $resolver,
|
||||
public array $arguments = [],
|
||||
public ?string $description = null,
|
||||
public bool $isDeprecated = false,
|
||||
public ?string $deprecationReason = null
|
||||
) {
|
||||
}
|
||||
|
||||
public function toDefinition(string $name): string
|
||||
{
|
||||
$def = $name;
|
||||
|
||||
// Add arguments if any
|
||||
if (! empty($this->arguments)) {
|
||||
$args = [];
|
||||
foreach ($this->arguments as $argName => $arg) {
|
||||
$args[] = $arg->toDefinition($argName);
|
||||
}
|
||||
$def .= '(' . implode(', ', $args) . ')';
|
||||
}
|
||||
|
||||
// Add return type
|
||||
$def .= ': ' . $this->type->toString();
|
||||
|
||||
// Add deprecation if needed
|
||||
if ($this->isDeprecated) {
|
||||
$reason = $this->deprecationReason ?? 'No longer supported';
|
||||
$def .= ' @deprecated(reason: "' . $reason . '")';
|
||||
}
|
||||
|
||||
return $def;
|
||||
}
|
||||
|
||||
public function resolve(mixed $root, array $args, mixed $context): mixed
|
||||
{
|
||||
return ($this->resolver)($root, $args, $context);
|
||||
}
|
||||
}
|
||||
72
src/Framework/GraphQL/GraphQLFieldType.php
Normal file
72
src/Framework/GraphQL/GraphQLFieldType.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents GraphQL field types (scalar and complex)
|
||||
*/
|
||||
final readonly class GraphQLFieldType
|
||||
{
|
||||
public function __construct(
|
||||
public string $type,
|
||||
public bool $isNullable = true,
|
||||
public bool $isList = false,
|
||||
public bool $isListNullable = true
|
||||
) {
|
||||
}
|
||||
|
||||
public function toString(): string
|
||||
{
|
||||
$typeStr = $this->type;
|
||||
|
||||
if ($this->isList) {
|
||||
$typeStr = $this->isNullable ? $typeStr : $typeStr . '!';
|
||||
$typeStr = '[' . $typeStr . ']';
|
||||
$typeStr = $this->isListNullable ? $typeStr : $typeStr . '!';
|
||||
} else {
|
||||
$typeStr = $this->isNullable ? $typeStr : $typeStr . '!';
|
||||
}
|
||||
|
||||
return $typeStr;
|
||||
}
|
||||
|
||||
// Common scalar types
|
||||
public static function string(bool $nullable = true): self
|
||||
{
|
||||
return new self('String', $nullable);
|
||||
}
|
||||
|
||||
public static function int(bool $nullable = true): self
|
||||
{
|
||||
return new self('Int', $nullable);
|
||||
}
|
||||
|
||||
public static function float(bool $nullable = true): self
|
||||
{
|
||||
return new self('Float', $nullable);
|
||||
}
|
||||
|
||||
public static function boolean(bool $nullable = true): self
|
||||
{
|
||||
return new self('Boolean', $nullable);
|
||||
}
|
||||
|
||||
public static function id(bool $nullable = false): self
|
||||
{
|
||||
return new self('ID', $nullable);
|
||||
}
|
||||
|
||||
// List types
|
||||
public static function listOf(string $type, bool $itemsNullable = true, bool $listNullable = true): self
|
||||
{
|
||||
return new self($type, $itemsNullable, true, $listNullable);
|
||||
}
|
||||
|
||||
// Custom type
|
||||
public static function custom(string $typeName, bool $nullable = true): self
|
||||
{
|
||||
return new self($typeName, $nullable);
|
||||
}
|
||||
}
|
||||
15
src/Framework/GraphQL/GraphQLOperationType.php
Normal file
15
src/Framework/GraphQL/GraphQLOperationType.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* GraphQL operation types
|
||||
*/
|
||||
enum GraphQLOperationType: string
|
||||
{
|
||||
case QUERY = 'query';
|
||||
case MUTATION = 'mutation';
|
||||
case SUBSCRIPTION = 'subscription';
|
||||
}
|
||||
42
src/Framework/GraphQL/GraphQLParsedField.php
Normal file
42
src/Framework/GraphQL/GraphQLParsedField.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents a parsed field from a GraphQL query
|
||||
*/
|
||||
final readonly class GraphQLParsedField
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed> $arguments
|
||||
* @param string[] $subFields
|
||||
*/
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public array $arguments = [],
|
||||
public array $subFields = []
|
||||
) {
|
||||
}
|
||||
|
||||
public function hasArguments(): bool
|
||||
{
|
||||
return ! empty($this->arguments);
|
||||
}
|
||||
|
||||
public function hasSubFields(): bool
|
||||
{
|
||||
return ! empty($this->subFields);
|
||||
}
|
||||
|
||||
public function getArgumentCount(): int
|
||||
{
|
||||
return count($this->arguments);
|
||||
}
|
||||
|
||||
public function getSubFieldCount(): int
|
||||
{
|
||||
return count($this->subFields);
|
||||
}
|
||||
}
|
||||
48
src/Framework/GraphQL/GraphQLParsedQuery.php
Normal file
48
src/Framework/GraphQL/GraphQLParsedQuery.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents a parsed GraphQL query
|
||||
*/
|
||||
final readonly class GraphQLParsedQuery
|
||||
{
|
||||
/**
|
||||
* @param GraphQLParsedField[] $fields
|
||||
* @param array<string, string> $variables
|
||||
*/
|
||||
public function __construct(
|
||||
public GraphQLOperationType $operationType,
|
||||
public array $fields,
|
||||
public array $variables,
|
||||
public string $rawQuery
|
||||
) {
|
||||
}
|
||||
|
||||
public function isQuery(): bool
|
||||
{
|
||||
return $this->operationType === GraphQLOperationType::QUERY;
|
||||
}
|
||||
|
||||
public function isMutation(): bool
|
||||
{
|
||||
return $this->operationType === GraphQLOperationType::MUTATION;
|
||||
}
|
||||
|
||||
public function isSubscription(): bool
|
||||
{
|
||||
return $this->operationType === GraphQLOperationType::SUBSCRIPTION;
|
||||
}
|
||||
|
||||
public function getFieldCount(): int
|
||||
{
|
||||
return count($this->fields);
|
||||
}
|
||||
|
||||
public function hasVariables(): bool
|
||||
{
|
||||
return ! empty($this->variables);
|
||||
}
|
||||
}
|
||||
167
src/Framework/GraphQL/GraphQLQueryParser.php
Normal file
167
src/Framework/GraphQL/GraphQLQueryParser.php
Normal file
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
use App\Framework\Exception\ErrorCode;
|
||||
use App\Framework\Exception\FrameworkException;
|
||||
|
||||
/**
|
||||
* Simple GraphQL query parser for basic queries and mutations
|
||||
*/
|
||||
final class GraphQLQueryParser
|
||||
{
|
||||
public function parse(string $query): GraphQLParsedQuery
|
||||
{
|
||||
$query = trim($query);
|
||||
|
||||
// Extract operation type (query or mutation)
|
||||
$operationType = $this->extractOperationType($query);
|
||||
|
||||
// Extract fields
|
||||
$fields = $this->extractFields($query);
|
||||
|
||||
// Extract variables if present
|
||||
$variables = $this->extractVariables($query);
|
||||
|
||||
return new GraphQLParsedQuery(
|
||||
operationType: $operationType,
|
||||
fields: $fields,
|
||||
variables: $variables,
|
||||
rawQuery: $query
|
||||
);
|
||||
}
|
||||
|
||||
private function extractOperationType(string $query): GraphQLOperationType
|
||||
{
|
||||
if (str_starts_with($query, 'query') || str_starts_with($query, '{')) {
|
||||
return GraphQLOperationType::QUERY;
|
||||
}
|
||||
|
||||
if (str_starts_with($query, 'mutation')) {
|
||||
return GraphQLOperationType::MUTATION;
|
||||
}
|
||||
|
||||
if (str_starts_with($query, 'subscription')) {
|
||||
return GraphQLOperationType::SUBSCRIPTION;
|
||||
}
|
||||
|
||||
throw FrameworkException::create(
|
||||
ErrorCode::VAL_BUSINESS_RULE_VIOLATION,
|
||||
'Invalid GraphQL operation type'
|
||||
);
|
||||
}
|
||||
|
||||
private function extractFields(string $query): array
|
||||
{
|
||||
// Simple regex to extract fields from query
|
||||
// This is a basic implementation - a real parser would be more sophisticated
|
||||
preg_match('/{([^}]+)}/', $query, $matches);
|
||||
|
||||
if (empty($matches[1])) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fieldsStr = trim($matches[1]);
|
||||
$fields = [];
|
||||
|
||||
// Parse each field (simplified)
|
||||
$lines = explode("\n", $fieldsStr);
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract field name and arguments
|
||||
if (preg_match('/^(\w+)(?:\(([^)]*)\))?(?:\s*{([^}]+)})?/', $line, $fieldMatch)) {
|
||||
$fieldName = $fieldMatch[1];
|
||||
$arguments = $this->parseArguments($fieldMatch[2] ?? '');
|
||||
$subFields = isset($fieldMatch[3]) ? $this->parseSubFields($fieldMatch[3]) : [];
|
||||
|
||||
$fields[] = new GraphQLParsedField(
|
||||
name: $fieldName,
|
||||
arguments: $arguments,
|
||||
subFields: $subFields
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
private function parseArguments(string $argsStr): array
|
||||
{
|
||||
if (empty($argsStr)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$args = [];
|
||||
$pairs = explode(',', $argsStr);
|
||||
|
||||
foreach ($pairs as $pair) {
|
||||
$pair = trim($pair);
|
||||
if (empty($pair)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[$key, $value] = explode(':', $pair, 2);
|
||||
$key = trim($key);
|
||||
$value = trim($value, ' "\'');
|
||||
|
||||
// Try to parse value as JSON for proper type conversion
|
||||
$decodedValue = json_decode($value, true);
|
||||
$args[$key] = $decodedValue !== null ? $decodedValue : $value;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
private function parseSubFields(string $subFieldsStr): array
|
||||
{
|
||||
$subFields = [];
|
||||
$lines = explode("\n", trim($subFieldsStr));
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$line = trim($line);
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$subFields[] = $line;
|
||||
}
|
||||
|
||||
return $subFields;
|
||||
}
|
||||
|
||||
private function extractVariables(string $query): array
|
||||
{
|
||||
// Extract variables from query definition
|
||||
if (preg_match('/\(([^)]+)\)/', $query, $matches)) {
|
||||
return $this->parseVariables($matches[1]);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
private function parseVariables(string $variablesStr): array
|
||||
{
|
||||
$variables = [];
|
||||
$pairs = explode(',', $variablesStr);
|
||||
|
||||
foreach ($pairs as $pair) {
|
||||
$pair = trim($pair);
|
||||
if (empty($pair) || ! str_starts_with($pair, '$')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse variable definition like $id: ID!
|
||||
if (preg_match('/\$(\w+):\s*(.+)/', $pair, $varMatch)) {
|
||||
$variables[$varMatch[1]] = trim($varMatch[2]);
|
||||
}
|
||||
}
|
||||
|
||||
return $variables;
|
||||
}
|
||||
}
|
||||
51
src/Framework/GraphQL/GraphQLResult.php
Normal file
51
src/Framework/GraphQL/GraphQLResult.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents a GraphQL execution result
|
||||
*/
|
||||
final readonly class GraphQLResult
|
||||
{
|
||||
/**
|
||||
* @param array<string, mixed>|null $data
|
||||
* @param array<array{message: string, extensions?: array}>|null $errors
|
||||
*/
|
||||
public function __construct(
|
||||
public ?array $data = null,
|
||||
public ?array $errors = null
|
||||
) {
|
||||
}
|
||||
|
||||
public function hasErrors(): bool
|
||||
{
|
||||
return ! empty($this->errors);
|
||||
}
|
||||
|
||||
public function hasData(): bool
|
||||
{
|
||||
return $this->data !== null;
|
||||
}
|
||||
|
||||
public function isSuccess(): bool
|
||||
{
|
||||
return ! $this->hasErrors() && $this->hasData();
|
||||
}
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
if ($this->hasData()) {
|
||||
$result['data'] = $this->data;
|
||||
}
|
||||
|
||||
if ($this->hasErrors()) {
|
||||
$result['errors'] = $this->errors;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
107
src/Framework/GraphQL/GraphQLSchema.php
Normal file
107
src/Framework/GraphQL/GraphQLSchema.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents a GraphQL schema with queries and mutations
|
||||
*/
|
||||
final class GraphQLSchema
|
||||
{
|
||||
/** @var array<string, GraphQLField> */
|
||||
public private(set) array $queries = [];
|
||||
|
||||
/** @var array<string, GraphQLField> */
|
||||
public private(set) array $mutations = [];
|
||||
|
||||
/** @var array<string, GraphQLType> */
|
||||
public private(set) array $types = [];
|
||||
|
||||
public string $schemaDefinition {
|
||||
get {
|
||||
$schema = [];
|
||||
|
||||
// Add type definitions
|
||||
foreach ($this->types as $name => $type) {
|
||||
$schema[] = $type->toDefinition();
|
||||
}
|
||||
|
||||
// Add Query type
|
||||
if (! empty($this->queries)) {
|
||||
$queryFields = [];
|
||||
foreach ($this->queries as $name => $field) {
|
||||
$queryFields[] = " " . $field->toDefinition($name);
|
||||
}
|
||||
$schema[] = "type Query {\n" . implode("\n", $queryFields) . "\n}";
|
||||
}
|
||||
|
||||
// Add Mutation type
|
||||
if (! empty($this->mutations)) {
|
||||
$mutationFields = [];
|
||||
foreach ($this->mutations as $name => $field) {
|
||||
$mutationFields[] = " " . $field->toDefinition($name);
|
||||
}
|
||||
$schema[] = "type Mutation {\n" . implode("\n", $mutationFields) . "\n}";
|
||||
}
|
||||
|
||||
return implode("\n\n", $schema);
|
||||
}
|
||||
}
|
||||
|
||||
public int $queryCount {
|
||||
get => count($this->queries);
|
||||
}
|
||||
|
||||
public int $mutationCount {
|
||||
get => count($this->mutations);
|
||||
}
|
||||
|
||||
public int $typeCount {
|
||||
get => count($this->types);
|
||||
}
|
||||
|
||||
public bool $hasQueries {
|
||||
get => ! empty($this->queries);
|
||||
}
|
||||
|
||||
public bool $hasMutations {
|
||||
get => ! empty($this->mutations);
|
||||
}
|
||||
|
||||
public function addQuery(string $name, GraphQLField $field): self
|
||||
{
|
||||
$this->queries[$name] = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addMutation(string $name, GraphQLField $field): self
|
||||
{
|
||||
$this->mutations[$name] = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addType(string $name, GraphQLType $type): self
|
||||
{
|
||||
$this->types[$name] = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuery(string $name): ?GraphQLField
|
||||
{
|
||||
return $this->queries[$name] ?? null;
|
||||
}
|
||||
|
||||
public function getMutation(string $name): ?GraphQLField
|
||||
{
|
||||
return $this->mutations[$name] ?? null;
|
||||
}
|
||||
|
||||
public function getType(string $name): ?GraphQLType
|
||||
{
|
||||
return $this->types[$name] ?? null;
|
||||
}
|
||||
}
|
||||
59
src/Framework/GraphQL/GraphQLType.php
Normal file
59
src/Framework/GraphQL/GraphQLType.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Framework\GraphQL;
|
||||
|
||||
/**
|
||||
* Represents a GraphQL type definition
|
||||
*/
|
||||
final class GraphQLType
|
||||
{
|
||||
/** @var array<string, GraphQLField> */
|
||||
public private(set) array $fields = [];
|
||||
|
||||
public function __construct(
|
||||
public readonly string $name,
|
||||
public readonly ?string $description = null,
|
||||
public readonly bool $isInput = false
|
||||
) {
|
||||
}
|
||||
|
||||
public int $fieldCount {
|
||||
get => count($this->fields);
|
||||
}
|
||||
|
||||
public bool $hasFields {
|
||||
get => ! empty($this->fields);
|
||||
}
|
||||
|
||||
public function addField(string $name, GraphQLField $field): self
|
||||
{
|
||||
$this->fields[$name] = $field;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getField(string $name): ?GraphQLField
|
||||
{
|
||||
return $this->fields[$name] ?? null;
|
||||
}
|
||||
|
||||
public function toDefinition(): string
|
||||
{
|
||||
$keyword = $this->isInput ? 'input' : 'type';
|
||||
$def = "{$keyword} {$this->name} {\n";
|
||||
|
||||
foreach ($this->fields as $fieldName => $field) {
|
||||
$def .= " " . $field->toDefinition($fieldName) . "\n";
|
||||
}
|
||||
|
||||
$def .= "}";
|
||||
|
||||
if ($this->description) {
|
||||
$def = '"""' . "\n" . $this->description . "\n" . '"""' . "\n" . $def;
|
||||
}
|
||||
|
||||
return $def;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user