Added DefinitionContainer interface to enable compositional use of user-land types (vs extending ObjectType, InterfaceType, etc). Very similar to IteratorAggregate vs Iterator

This commit is contained in:
vladar 2016-10-23 02:00:37 +07:00
parent c11f25794a
commit 9941a0143a
10 changed files with 78 additions and 12 deletions

View File

@ -19,6 +19,7 @@ use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\ResolveInfo;
use GraphQL\Type\Definition\ScalarType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Type\Introspection;
use GraphQL\Utils;
@ -744,6 +745,10 @@ class Executor
$runtimeType = $exeContext->schema->getType($runtimeType);
}
if ($runtimeType instanceof DefinitionContainer) {
$runtimeType = $runtimeType->getDefinition();
}
if (!($runtimeType instanceof ObjectType)) {
throw new Error(
"Abstract type {$returnType} must resolve to an Object type at runtime " .

View File

@ -9,6 +9,7 @@ use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\UnionType;
use GraphQL\Type\Definition\WrappingType;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Type\Introspection;
/**
@ -113,6 +114,18 @@ class Schema
'validate' => true
];
if ($config['query'] instanceof DefinitionContainer) {
$config['query'] = $config['query']->getDefinition();
}
if ($config['mutation'] instanceof DefinitionContainer) {
$config['mutation'] = $config['mutation']->getDefinition();
}
if ($config['subscription'] instanceof DefinitionContainer) {
$config['subscription'] = $config['subscription']->getDefinition();
}
Utils::invariant(
$config['query'] instanceof ObjectType,
"Schema query must be Object Type but got: " . Utils::getVariableType($config['query'])
@ -299,6 +312,9 @@ class Schema
if (!$type) {
return $this->typeMap;
}
if ($type instanceof DefinitionContainer) {
$type = $type->getDefinition();
}
if ($type instanceof WrappingType) {
return $this->extractTypes($type->getWrappedType(true));

View File

@ -2,6 +2,7 @@
namespace GraphQL\Type\Definition;
use GraphQL\Error\InvariantViolation;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Utils;
/**
@ -188,6 +189,10 @@ class Config
$err = 'Error in "'.$typeName.'" type definition: ' . "Each entry at '$pathStr' must be an array, but '%s' is '%s'";
foreach ($value as $arrKey => $arrValue) {
if ($arrValue instanceof DefinitionContainer) {
$arrValue = $arrValue->getDefinition();
}
if (is_array($def->definition)) {
if ($def->flags & self::MAYBE_TYPE && $arrValue instanceof Type) {
$arrValue = ['type' => $arrValue];
@ -220,6 +225,10 @@ class Config
return ; // Allow nulls for non-required fields
}
if ($value instanceof DefinitionContainer) {
$value = $value->getDefinition();
}
switch (true) {
case $def & self::ANY:
break;

View File

@ -52,7 +52,7 @@ class InterfaceType extends Type implements AbstractType, OutputType, CompositeT
}
/**
* @return array<FieldDefinition>
* @return FieldDefinition[]
*/
public function getFields()
{

View File

@ -1,6 +1,7 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Utils;
/**
@ -15,12 +16,12 @@ class ListOfType extends Type implements WrappingType, OutputType, InputType
public $ofType;
/**
* @param callable|Type $type
* @param callable|Type|DefinitionContainer $type
*/
public function __construct($type)
{
Utils::invariant(
$type instanceof Type || is_callable($type),
$type instanceof Type || $type instanceof DefinitionContainer || is_callable($type),
'Expecting instance of GraphQL\Type\Definition\Type or callable returning instance of that class'
);

View File

@ -1,6 +1,7 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Type\TypeKind;
use GraphQL\Utils;
@ -16,13 +17,13 @@ class NonNull extends Type implements WrappingType, OutputType, InputType
protected $ofType;
/**
* @param callable|Type $type
* @param callable|Type|DefinitionContainer $type
* @throws \Exception
*/
public function __construct($type)
{
Utils::invariant(
$type instanceof Type || is_callable($type),
$type instanceof Type || $type instanceof DefinitionContainer || is_callable($type),
'Expecting instance of GraphQL\Type\Definition\Type or callable returning instance of that class'
);
Utils::invariant(

View File

@ -1,5 +1,6 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Utils;
@ -139,6 +140,12 @@ class ObjectType extends Type implements OutputType, CompositeType
if (null === $this->interfaces) {
$interfaces = isset($this->config['interfaces']) ? $this->config['interfaces'] : [];
$interfaces = is_callable($interfaces) ? call_user_func($interfaces) : $interfaces;
// TODO: Return some sort of generator to avoid multiple loops
$interfaces = Utils::map($interfaces, function($iface) {
return $iface instanceof DefinitionContainer ? $iface->getDefinition() : $iface;
});
$this->interfaces = $interfaces;
}
return $this->interfaces;

View File

@ -2,6 +2,7 @@
namespace GraphQL\Type\Definition;
use GraphQL\Error\InvariantViolation;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Utils;
/*
@ -198,6 +199,9 @@ abstract class Type
);
$type = $type();
}
if ($type instanceof DefinitionContainer) {
$type = $type->getDefinition();
}
if (!$type instanceof Type) {
throw new InvariantViolation(sprintf(

View File

@ -1,6 +1,7 @@
<?php
namespace GraphQL\Type\Definition;
use GraphQL\Type\DefinitionContainer;
use GraphQL\Utils;
/**
@ -42,8 +43,6 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType
'description' => Config::STRING
]);
Utils::invariant(!empty($config['types']), "");
/**
* Optionally provide a custom type resolver function. If one is not provided,
* the default implemenation will call `isTypeOf` on each implementing
@ -51,7 +50,6 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType
*/
$this->name = $config['name'];
$this->description = isset($config['description']) ? $config['description'] : null;
$this->types = $config['types'];
$this->resolveTypeFn = isset($config['resolveType']) ? $config['resolveType'] : null;
$this->config = $config;
}
@ -70,13 +68,23 @@ class UnionType extends Type implements AbstractType, OutputType, CompositeType
*/
public function getTypes()
{
if ($this->types instanceof \Closure) {
$this->types = call_user_func($this->types);
if (null === $this->types) {
if ($this->config['types'] instanceof \Closure) {
$types = call_user_func($this->config['types']);
} else {
$types = $this->config['types'];
}
Utils::invariant(
is_array($this->types),
'Closure for option "types" of union "%s" is expected to return array of types',
is_array($types),
'Option "types" of union "%s" is expected to return array of types (or closure returning array of types)',
$this->name
);
// TODO: Return some sort of generator to avoid multiple loops
$this->types = Utils::map($types, function($type) {
return $type instanceof DefinitionContainer ? $type->getDefinition() : $type;
});
}
return $this->types;
}

View File

@ -0,0 +1,15 @@
<?php
namespace GraphQL\Type;
use GraphQL\Type\Definition\Type;
/**
* Interface DefinitionContainer
* @package GraphQL\Type
*/
interface DefinitionContainer
{
/**
* @return Type
*/
public function getDefinition();
}