Skip to content

Instantly share code, notes, and snippets.

@Jalle19
Created May 15, 2019 12:48
Show Gist options
  • Save Jalle19/fc17e8046fa1d530c02c80c337e47e96 to your computer and use it in GitHub Desktop.
Save Jalle19/fc17e8046fa1d530c02c80c337e47e96 to your computer and use it in GitHub Desktop.
Example of advanced graphql-php usage
<?php
namespace Foo\Bar\GraphQL;
use Digia\GraphQL\Error\Handler\ErrorHandlerInterface;
use Digia\GraphQL\Error\InvariantException;
use Digia\GraphQL\Execution\ExecutionResult;
use Digia\GraphQL\Language\Node\DocumentNode;
use Digia\GraphQL\Language\ParserInterface;
use Digia\GraphQL\Language\Source;
use Digia\GraphQL\Language\SyntaxErrorException;
use Digia\GraphQL\Schema\Building\SchemaBuilderInterface;
use Digia\GraphQL\Schema\Resolver\ResolverRegistryInterface;
use Digia\GraphQL\Schema\Schema;
use Digia\GraphQL\Schema\Validation\SchemaValidationException;
use Illuminate\Contracts\Cache\Repository as CacheRepository;
use function Digia\GraphQL\execute;
use function Digia\GraphQL\parse;
use function Digia\GraphQL\validate;
use function Digia\GraphQL\validateSchema;
class GraphQLService
{
private const SCHEMA_DOCUMENT_CACHE_KEY = 'graphql_schema_document';
private const QUERY_VALIDATION_CACHE_KEY = 'graphql_query_validation';
/**
* @var CacheRepository
*/
private $cacheRepository;
/**
* @var SchemaBuilderInterface
*/
private $schemaBuilder;
/**
* @var ParserInterface
*/
private $parser;
/**
* @var ErrorHandlerInterface
*/
private $errorHandler;
/**
* @var Source
*/
private $schemaDefinition;
/**
* @var ResolverRegistryInterface
*/
private $resolverRegistry;
/**
* @var array
*/
private $options;
/**
* GraphQLService constructor.
*
* @param CacheRepository $cacheRepository
* @param SchemaBuilderInterface $schemaBuilder
* @param ParserInterface $parser
* @param ErrorHandlerInterface $errorHandler
* @param Source $schemaDefinition
* @param ResolverRegistryInterface $resolverRegistry
* @param array $options
*/
public function __construct(
CacheRepository $cacheRepository,
SchemaBuilderInterface $schemaBuilder,
ParserInterface $parser,
ErrorHandlerInterface $errorHandler,
Source $schemaDefinition,
ResolverRegistryInterface $resolverRegistry,
array $options
) {
$this->cacheRepository = $cacheRepository;
$this->schemaDefinition = $schemaDefinition;
$this->schemaBuilder = $schemaBuilder;
$this->parser = $parser;
$this->resolverRegistry = $resolverRegistry;
$this->options = $options;
$this->errorHandler = $errorHandler;
}
/**
* @param string $query
* @param array $variables
* @param null|string $operationName
*
* @return array
* @throws InvariantException
*/
public function executeQuery(string $query, array $variables, ?string $operationName): array
{
// Build the schema
$schemaDocument = $this->getSchemaDocument();
$schema = $this->schemaBuilder->build($schemaDocument, $this->resolverRegistry, $this->options);
// Validate the schema
$schemaValidationErrors = $this->validateSchema($schema);
if (!empty($schemaValidationErrors)) {
return (new ExecutionResult([], $schemaValidationErrors))->toArray();
}
// Parse and validate the query
$documentOrErrors = $this->parseAndValidateQuery($schema, $query);
if (\is_array($documentOrErrors)) {
return $documentOrErrors;
}
// Execute the query and return the results
$result = execute(
$schema,
$documentOrErrors,
null,
null,
$variables,
$operationName,
null,
$this->errorHandler
);
return $result->toArray();
}
/**
* @return DocumentNode
*/
protected function getSchemaDocument(): DocumentNode
{
$document = $this->cacheRepository->get(self::SCHEMA_DOCUMENT_CACHE_KEY);
if ($document === null) {
$document = $this->parser->parse($this->schemaDefinition, $this->options);
$this->cacheRepository->forever(self::SCHEMA_DOCUMENT_CACHE_KEY, $document);
}
return $document;
}
/**
* Validates the schema and returns any eventual errors from the validation. This method does nothing unless
* APP_ENV=local
*
* @param Schema $schema
*
* @return SchemaValidationException[]
*/
protected function validateSchema(Schema $schema): array
{
// Validate the schema when developing locally
if (env('APP_ENV', 'local') === 'local') {
return validateSchema($schema);
}
return [];
}
/**
* @param Schema $schema
* @param string $query
*
* @return DocumentNode|array the parsed and validated document, or a list of errors
*
* @throws InvariantException
*/
protected function parseAndValidateQuery(Schema $schema, string $query)
{
// Parse the query
try {
$document = parse($query);
} catch (SyntaxErrorException $error) {
return (new ExecutionResult([], [$error]))->toArray();
}
// Validate the query once
if (!$this->isValidatedQuery($query)) {
$validationErrors = validate($schema, $document);
if (!empty($validationErrors)) {
return (new ExecutionResult([], $validationErrors))->toArray();
}
$this->markQueryAsValidated($query);
}
return $document;
}
/**
* @param string $query
*
* @return bool
*/
protected function isValidatedQuery(string $query): bool
{
$validatedQueryHashes = $this->cacheRepository->get(self::QUERY_VALIDATION_CACHE_KEY, []);
$queryHash = self::calculateQueryHash($query);
return \in_array($queryHash, $validatedQueryHashes, true);
}
/**
* @param string $query
*/
protected function markQueryAsValidated(string $query): void
{
$validatedQueryHashes = $this->cacheRepository->get(self::QUERY_VALIDATION_CACHE_KEY, []);
$queryHash = self::calculateQueryHash($query);
$validatedQueryHashes[] = $queryHash;
$this->cacheRepository->forever(self::QUERY_VALIDATION_CACHE_KEY, $validatedQueryHashes);
}
/**
* @param string $query
*
* @return string
*/
protected static function calculateQueryHash(string $query): string
{
return \md5($query);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment