Almost all of the GraphQL types you define will be object types. Object types have a name, but most importantly describe their fields. Example: var AddressType = new GraphQLObjectType({ name: 'Address', fields: { street: { type: GraphQLString }, number: { type: GraphQLInt }, formatted: { type: GraphQLString, resolve(obj) { return obj.number + ' ' + obj.street } } } }); When two types need to refer to each other, or a type needs to refer to itself in a field, you can use a function expression (aka a closure or a thunk) to supply the fields lazily. Example: var PersonType = new GraphQLObjectType({ name: 'Person', fields: () => ({ name: { type: GraphQLString }, bestFriend: { type: PersonType }, }) });
Inheritance: extends Type, implements OutputType, implements CompositeType
Example #1
0
 /**
  * @param ObjectType $object
  * @param InterfaceType $iface
  * @throws \Exception
  */
 private function assertObjectImplementsInterface(ObjectType $object, InterfaceType $iface)
 {
     $objectFieldMap = $object->getFields();
     $ifaceFieldMap = $iface->getFields();
     foreach ($ifaceFieldMap as $fieldName => $ifaceField) {
         Utils::invariant(isset($objectFieldMap[$fieldName]), "\"{$iface}\" expects field \"{$fieldName}\" but \"{$object}\" does not provide it");
         /** @var $ifaceField FieldDefinition */
         /** @var $objectField FieldDefinition */
         $objectField = $objectFieldMap[$fieldName];
         Utils::invariant($this->isEqualType($ifaceField->getType(), $objectField->getType()), "{$iface}.{$fieldName} expects type \"{$ifaceField->getType()}\" but " . "{$object}.{$fieldName} provides type \"{$objectField->getType()}");
         foreach ($ifaceField->args as $ifaceArg) {
             /** @var $ifaceArg FieldArgument */
             /** @var $objectArg FieldArgument */
             $argName = $ifaceArg->name;
             $objectArg = $objectField->getArg($argName);
             // Assert interface field arg exists on object field.
             Utils::invariant($objectArg, "{$iface}.{$fieldName} expects argument \"{$argName}\" but {$object}.{$fieldName} does not provide it.");
             // Assert interface field arg type matches object field arg type.
             // (invariant)
             Utils::invariant($this->isEqualType($ifaceArg->getType(), $objectArg->getType()), "{$iface}.{$fieldName}({$argName}:) expects type \"{$ifaceArg->getType()}\" " . "but {$object}.{$fieldName}({$argName}:) provides " . "type \"{$objectArg->getType()}\"");
             // Assert argument set invariance.
             foreach ($objectField->args as $objectArg) {
                 $argName = $objectArg->name;
                 $ifaceArg = $ifaceField->getArg($argName);
                 Utils::invariant($ifaceArg, "{$iface}.{$fieldName} does not define argument \"{$argName}\" but " . "{$object}.{$fieldName} provides it.");
             }
         }
     }
 }
 /**
  * Determine if output fields should be included.
  *
  * @param  mixed $objectType
  * @return boolean
  */
 protected function includeOutputFields(ObjectType $objectType)
 {
     $fields = [];
     foreach ($objectType->getFields() as $name => $field) {
         $type = $field->getType();
         if ($type instanceof ObjectType) {
             $config = $type->config;
             if (isset($config['name']) && preg_match('/Connection$/', $config['name'])) {
                 continue;
             }
         }
         $fields[] = $name;
     }
     return $fields;
 }
Example #3
0
 public function __construct()
 {
     $config = ['name' => 'ImageType', 'fields' => ['id' => Types::id(), 'type' => new EnumType(['name' => 'ImageTypeEnum', 'values' => ['USERPIC' => Image::TYPE_USERPIC]]), 'size' => Types::imageSizeEnum(), 'width' => Types::int(), 'height' => Types::int(), 'url' => ['type' => Types::url(), 'resolve' => [$this, 'resolveUrl']], 'fieldWithError' => ['type' => Types::string(), 'resolve' => function () {
         throw new \Exception("Field with exception");
     }], 'nonNullFieldWithError' => ['type' => Types::nonNull(Types::string()), 'resolve' => function () {
         throw new \Exception("Non-null field with exception");
     }]]];
     parent::__construct($config);
 }
Example #4
0
 public function __construct()
 {
     $config = ['name' => 'Query', 'fields' => ['user' => ['type' => Types::user(), 'description' => 'Returns user by id (in range of 1-5)', 'args' => ['id' => Types::nonNull(Types::id())]], 'viewer' => ['type' => Types::user(), 'description' => 'Represents currently logged-in user (for the sake of example - simply returns user with id == 1)'], 'stories' => ['type' => Types::listOf(Types::story()), 'description' => 'Returns subset of stories posted for this blog', 'args' => ['after' => ['type' => Types::id(), 'description' => 'Fetch stories listed after the story with this ID'], 'limit' => ['type' => Types::int(), 'description' => 'Number of stories to be returned', 'defaultValue' => 10]]], 'lastStoryPosted' => ['type' => Types::story(), 'description' => 'Returns last story posted for this blog'], 'deprecatedField' => ['type' => Types::string(), 'deprecationReason' => 'This field is deprecated!'], 'fieldWithException' => ['type' => Types::string(), 'resolve' => function () {
         throw new \Exception("Exception message thrown in field resolver");
     }], 'hello' => Type::string()], 'resolveField' => function ($val, $args, $context, ResolveInfo $info) {
         return $this->{$info->fieldName}($val, $args, $context, $info);
     }];
     parent::__construct($config);
 }
Example #5
0
 public function testDefinesAMutationSchema()
 {
     $schema = new Schema($this->blogQuery, $this->blogMutation);
     $this->assertSame($this->blogMutation, $schema->getMutationType());
     $writeMutation = $this->blogMutation->getField('writeArticle');
     $this->assertInstanceOf('GraphQL\\Type\\Definition\\FieldDefinition', $writeMutation);
     $this->assertSame($this->blogArticle, $writeMutation->getType());
     $this->assertSame('Article', $writeMutation->getType()->name);
     $this->assertSame('writeArticle', $writeMutation->name);
 }
Example #6
0
 public function __construct()
 {
     // Option #2: define type using inheritance, see any other object type for compositional example
     $config = ['name' => 'ImageType', 'fields' => ['id' => Types::id(), 'type' => new EnumType(['name' => 'ImageTypeEnum', 'values' => ['USERPIC' => Image::TYPE_USERPIC]]), 'size' => Types::imageSizeEnum(), 'width' => Types::int(), 'height' => Types::int(), 'url' => ['type' => Types::url(), 'resolve' => [$this, 'resolveUrl']], 'fieldWithError' => ['type' => Types::string(), 'resolve' => function () {
         throw new \Exception("Field with exception");
     }], 'nonNullFieldWithError' => ['type' => Types::nonNull(Types::string()), 'resolve' => function () {
         throw new \Exception("Non-null field with exception");
     }]]];
     parent::__construct($config);
 }
Example #7
0
 public function __construct()
 {
     $config = ['name' => 'Story', 'fields' => function () {
         return ['id' => Types::id(), 'author' => Types::user(), 'mentions' => Types::listOf(Types::mention()), 'totalCommentCount' => Types::int(), 'comments' => ['type' => Types::listOf(Types::comment()), 'args' => ['after' => ['type' => Types::id(), 'description' => 'Load all comments listed after given comment ID'], 'limit' => ['type' => Types::int(), 'defaultValue' => 5]]], 'likes' => ['type' => Types::listOf(Types::user()), 'args' => ['limit' => ['type' => Types::int(), 'description' => 'Limit the number of recent likes returned', 'defaultValue' => 5]]], 'likedBy' => ['type' => Types::listOf(Types::user())], 'affordances' => Types::listOf(new EnumType(['name' => 'StoryAffordancesEnum', 'values' => [self::EDIT, self::DELETE, self::LIKE, self::UNLIKE, self::REPLY]])), 'hasViewerLiked' => Types::boolean(), Types::htmlField('body')];
     }, 'interfaces' => [Types::node()], 'resolveField' => function ($value, $args, $context, ResolveInfo $info) {
         if (method_exists($this, $info->fieldName)) {
             return $this->{$info->fieldName}($value, $args, $context, $info);
         } else {
             return $value->{$info->fieldName};
         }
     }];
     parent::__construct($config);
 }
Example #8
0
 public function __construct()
 {
     $config = ['name' => 'Comment', 'fields' => function () {
         return ['id' => Types::id(), 'author' => Types::user(), 'parent' => Types::comment(), 'isAnonymous' => Types::boolean(), 'replies' => ['type' => Types::listOf(Types::comment()), 'args' => ['after' => Types::int(), 'limit' => ['type' => Types::int(), 'defaultValue' => 5]]], 'totalReplyCount' => Types::int(), Types::htmlField('body')];
     }, 'resolveField' => function ($value, $args, $context, ResolveInfo $info) {
         if (method_exists($this, $info->fieldName)) {
             return $this->{$info->fieldName}($value, $args, $context, $info);
         } else {
             return $value->{$info->fieldName};
         }
     }];
     parent::__construct($config);
 }
Example #9
0
 public function __construct()
 {
     $config = ['name' => 'User', 'description' => 'Our blog authors', 'fields' => function () {
         return ['id' => Types::id(), 'email' => Types::email(), 'photo' => ['type' => Types::image(), 'description' => 'User photo URL', 'args' => ['size' => Types::nonNull(Types::imageSizeEnum())]], 'firstName' => ['type' => Types::string()], 'lastName' => ['type' => Types::string()], 'lastStoryPosted' => Types::story(), 'fieldWithError' => ['type' => Types::string(), 'resolve' => function () {
             throw new \Exception("This is error field");
         }]];
     }, 'interfaces' => [Types::node()], 'resolveField' => function ($value, $args, $context, ResolveInfo $info) {
         if (method_exists($this, $info->fieldName)) {
             return $this->{$info->fieldName}($value, $args, $context, $info);
         } else {
             return $value->{$info->fieldName};
         }
     }];
     parent::__construct($config);
 }
Example #10
0
 public function testInputObjectTypeAllowsRecursiveDefinitions()
 {
     $called = false;
     $inputObject = new InputObjectType(['name' => 'InputObject', 'fields' => function () use(&$inputObject, &$called) {
         $called = true;
         return ['value' => ['type' => Type::string()], 'nested' => ['type' => $inputObject]];
     }]);
     $someMutation = new ObjectType(['name' => 'SomeMutation', 'fields' => ['mutateSomething' => ['type' => $this->blogArticle, 'args' => ['input' => ['type' => $inputObject]]]]]);
     $schema = new Schema(['query' => $this->blogQuery, 'mutation' => $someMutation]);
     $this->assertTrue($called);
     $this->assertSame($inputObject, $schema->getType('InputObject'));
     $this->assertEquals(count($inputObject->getFields()), 2);
     $this->assertSame($inputObject->getField('nested')->getType(), $inputObject);
     $this->assertSame($someMutation->getField('mutateSomething')->getArg('input')->getType(), $inputObject);
 }
Example #11
0
 /**
  * Complete an Object value by executing all sub-selections.
  *
  * @param ExecutionContext $exeContext
  * @param ObjectType $returnType
  * @param $fieldNodes
  * @param ResolveInfo $info
  * @param array $path
  * @param $result
  * @return array|Promise|\stdClass
  * @throws Error
  */
 private static function completeObjectValue(ExecutionContext $exeContext, ObjectType $returnType, $fieldNodes, ResolveInfo $info, $path, &$result)
 {
     // If there is an isTypeOf predicate function, call it with the
     // current result. If isTypeOf returns false, then raise an error rather
     // than continuing execution.
     if (false === $returnType->isTypeOf($result, $exeContext->contextValue, $info)) {
         throw new Error("Expected value of type {$returnType} but got: " . Utils::getVariableType($result), $fieldNodes);
     }
     // Collect sub-fields to execute to complete this value.
     $subFieldNodes = new \ArrayObject();
     $visitedFragmentNames = new \ArrayObject();
     foreach ($fieldNodes as $fieldNode) {
         if (isset($fieldNode->selectionSet)) {
             $subFieldNodes = self::collectFields($exeContext, $returnType, $fieldNode->selectionSet, $subFieldNodes, $visitedFragmentNames);
         }
     }
     return self::executeFields($exeContext, $returnType, $result, $path, $subFieldNodes);
 }
Example #12
0
 /**
  * This method looks up the field on the given type defintion.
  * It has special casing for the two introspection fields, __schema
  * and __typename. __typename is special because it can always be
  * queried as a field, even in situations where no other fields
  * are allowed, like on a Union. __schema could get automatically
  * added to the query type, but that would require mutating type
  * definitions, which would cause issues.
  *
  * @return FieldDefinition
  */
 private static function getFieldDef(Schema $schema, ObjectType $parentType, $fieldName)
 {
     $schemaMetaFieldDef = Introspection::schemaMetaFieldDef();
     $typeMetaFieldDef = Introspection::typeMetaFieldDef();
     $typeNameMetaFieldDef = Introspection::typeNameMetaFieldDef();
     if ($fieldName === $schemaMetaFieldDef->name && $schema->getQueryType() === $parentType) {
         return $schemaMetaFieldDef;
     } else {
         if ($fieldName === $typeMetaFieldDef->name && $schema->getQueryType() === $parentType) {
             return $typeMetaFieldDef;
         } else {
             if ($fieldName === $typeNameMetaFieldDef->name) {
                 return $typeNameMetaFieldDef;
             }
         }
     }
     $tmp = $parentType->getFields();
     return isset($tmp[$fieldName]) ? $tmp[$fieldName] : null;
 }
Example #13
0
 /**
  * Update the interfaces to know about this implementation.
  * This is an rare and unfortunate use of mutation in the type definition
  * implementations, but avoids an expensive "getPossibleTypes"
  * implementation for Interface types.
  *
  * @param ObjectType $impl
  * @param InterfaceType[] $interfaces
  */
 public static function addImplementationToInterfaces(ObjectType $impl)
 {
     foreach ($impl->getInterfaces() as $interface) {
         $interface->_implementations[] = $impl;
     }
 }
Example #14
0
 public function testAllowsRecursiveDefinitions()
 {
     // See https://github.com/webonyx/graphql-php/issues/16
     $node = new InterfaceType(['name' => 'Node', 'fields' => ['id' => ['type' => Type::nonNull(Type::id())]]]);
     $blog = null;
     $called = false;
     $user = new ObjectType(['name' => 'User', 'fields' => function () use(&$blog, &$called) {
         $this->assertNotNull($blog, 'Blog type is expected to be defined at this point, but it is null');
         $called = true;
         return ['id' => ['type' => Type::nonNull(Type::id())], 'blogs' => ['type' => Type::nonNull(Type::listOf(Type::nonNull($blog)))]];
     }, 'interfaces' => function () use($node) {
         return [$node];
     }]);
     $blog = new ObjectType(['name' => 'Blog', 'fields' => function () use($user) {
         return ['id' => ['type' => Type::nonNull(Type::id())], 'owner' => ['type' => Type::nonNull($user)]];
     }, 'interfaces' => function () use($node) {
         return [$node];
     }]);
     $schema = new Schema(new ObjectType(['name' => 'Query', 'fields' => ['node' => ['type' => $node]]]));
     $this->assertTrue($called);
     $this->assertEquals([$node], $blog->getInterfaces());
     $this->assertEquals([$node], $user->getInterfaces());
     $this->assertNotNull($user->getField('blogs'));
     $this->assertSame($blog, $user->getField('blogs')->getType()->getWrappedType(true));
     $this->assertNotNull($blog->getField('owner'));
     $this->assertSame($user, $blog->getField('owner')->getType()->getWrappedType(true));
 }
Example #15
0
 /**
  * Add edge instance.
  *
  * @param  ObjectType $type
  * @param  string     $name
  * @return void
  */
 public function addEdge(ObjectType $type, $name)
 {
     if ($edges = $type->getField('edges')) {
         $type = $edges->getType()->getWrappedType();
         $this->edgeInstances->put($name, $type);
     }
 }
Example #16
0
 public function __construct()
 {
     $config = ['fields' => ['b' => Type::string()]];
     parent::__construct($config);
 }
Example #17
0
 /**
  * @param FieldContainer $type
  * @return GQLDefinition\ObjectType
  */
 private function createType(FieldContainer $type)
 {
     if (null !== $type->getFields()) {
         $this->prepareFields($type->getFields());
     }
     $type = new GQLDefinition\ObjectType($type->toMapping());
     return $type;
 }
Example #18
0
 function __construct($config)
 {
     parent::__construct($this->getConfig($config));
 }