/** * @param string $name * @return Type */ public function resolveType($name) { if (preg_match('#^(.+)\\!$#', $name, $regs)) { return Type::nonNull($this->resolveType($regs[1])); } if (preg_match('#^\\[(.+)\\]$#', $name, $regs)) { return Type::listOf($this->resolveType($regs[1])); } switch ($name) { case Type::INT: return Type::int(); case Type::STRING: return Type::string(); case Type::BOOLEAN: return Type::boolean(); case Type::FLOAT: return Type::float(); case Type::ID: return Type::id(); default: if (!isset($this->types[$name])) { throw new \InvalidArgumentException(sprintf('Type "%s" is not defined', $name)); } return $this->types[$name]; } }
/** * @return Schema */ protected function getDefaultSchema() { $Being = new InterfaceType(['name' => 'Being', 'fields' => ['name' => ['type' => Type::string()]]]); $Pet = new InterfaceType(['name' => 'Pet', 'fields' => ['name' => ['type' => Type::string()]]]); $DogCommand = new EnumType(['name' => 'DogCommand', 'values' => ['SIT' => ['value' => 0], 'HEEL' => ['value' => 1], 'DOWN' => ['value' => 3]]]); $Dog = new ObjectType(['name' => 'Dog', 'fields' => ['name' => ['type' => Type::string()], 'nickname' => ['type' => Type::string()], 'barkVolume' => ['type' => Type::int()], 'barks' => ['type' => Type::boolean()], 'doesKnowCommand' => ['type' => Type::boolean(), 'args' => ['dogCommand' => ['type' => $DogCommand]]], 'isHousetrained' => ['type' => Type::boolean(), 'args' => ['atOtherHomes' => ['type' => Type::boolean(), 'defaultValue' => true]]], 'isAtLocation' => ['type' => Type::boolean(), 'args' => ['x' => ['type' => Type::int()], 'y' => ['type' => Type::int()]]]], 'interfaces' => [$Being, $Pet]]); $FurColor = new EnumType(['name' => 'FurColor', 'values' => ['BROWN' => ['value' => 0], 'BLACK' => ['value' => 1], 'TAN' => ['value' => 2], 'SPOTTED' => ['value' => 3]]]); $Cat = new ObjectType(['name' => 'Cat', 'fields' => ['name' => ['type' => Type::string()], 'nickname' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], 'meowVolume' => ['type' => Type::int()], 'furColor' => ['type' => $FurColor]], 'interfaces' => [$Being, $Pet]]); $CatOrDog = new UnionType(['name' => 'CatOrDog', 'types' => [$Dog, $Cat], 'resolveType' => function ($value) { // not used for validation return null; }]); $Intelligent = new InterfaceType(['name' => 'Intelligent', 'fields' => ['iq' => ['type' => Type::int()]]]); $Human = $this->humanType = new ObjectType(['name' => 'Human', 'interfaces' => [$Being, $Intelligent], 'fields' => ['name' => ['args' => ['surname' => ['type' => Type::boolean()]], 'type' => Type::string()], 'pets' => ['type' => Type::listOf($Pet)], 'relatives' => ['type' => function () { return Type::listOf($this->humanType); }]]]); $Alien = new ObjectType(['name' => 'Alien', 'interfaces' => [$Being, $Intelligent], 'fields' => ['iq' => ['type' => Type::int()], 'numEyes' => ['type' => Type::int()]]]); $DogOrHuman = new UnionType(['name' => 'DogOrHuman', 'types' => [$Dog, $Human], 'resolveType' => function () { // not used for validation return null; }]); $HumanOrAlien = new UnionType(['name' => 'HumanOrAlien', 'types' => [$Human, $Alien], 'resolveType' => function () { // not used for validation return null; }]); $ComplexInput = new InputObjectType(['name' => 'ComplexInput', 'fields' => ['requiredField' => ['type' => Type::nonNull(Type::boolean())], 'intField' => ['type' => Type::int()], 'stringField' => ['type' => Type::string()], 'booleanField' => ['type' => Type::boolean()], 'stringListField' => ['type' => Type::listOf(Type::string())]]]); $ComplicatedArgs = new ObjectType(['name' => 'ComplicatedArgs', 'fields' => ['intArgField' => ['type' => Type::string(), 'args' => ['intArg' => ['type' => Type::int()]]], 'nonNullIntArgField' => ['type' => Type::string(), 'args' => ['nonNullIntArg' => ['type' => Type::nonNull(Type::int())]]], 'stringArgField' => ['type' => Type::string(), 'args' => ['stringArg' => ['type' => Type::string()]]], 'booleanArgField' => ['type' => Type::string(), 'args' => ['booleanArg' => ['type' => Type::boolean()]]], 'enumArgField' => ['type' => Type::string(), 'args' => ['enumArg' => ['type' => $FurColor]]], 'floatArgField' => ['type' => Type::string(), 'args' => ['floatArg' => ['type' => Type::float()]]], 'idArgField' => ['type' => Type::string(), 'args' => ['idArg' => ['type' => Type::id()]]], 'stringListArgField' => ['type' => Type::string(), 'args' => ['stringListArg' => ['type' => Type::listOf(Type::string())]]], 'complexArgField' => ['type' => Type::string(), 'args' => ['complexArg' => ['type' => $ComplexInput]]], 'multipleReqs' => ['type' => Type::string(), 'args' => ['req1' => ['type' => Type::nonNull(Type::int())], 'req2' => ['type' => Type::nonNull(Type::int())]]], 'multipleOpts' => ['type' => Type::string(), 'args' => ['opt1' => ['type' => Type::int(), 'defaultValue' => 0], 'opt2' => ['type' => Type::int(), 'defaultValue' => 0]]], 'multipleOptAndReq' => ['type' => Type::string(), 'args' => ['req1' => ['type' => Type::nonNull(Type::int())], 'req2' => ['type' => Type::nonNull(Type::int())], 'opt1' => ['type' => Type::int(), 'defaultValue' => 0], 'opt2' => ['type' => Type::int(), 'defaultValue' => 0]]]]]); $queryRoot = new ObjectType(['name' => 'QueryRoot', 'fields' => ['human' => ['args' => ['id' => ['type' => Type::id()]], 'type' => $Human], 'alien' => ['type' => $Alien], 'dog' => ['type' => $Dog], 'cat' => ['type' => $Cat], 'pet' => ['type' => $Pet], 'catOrDog' => ['type' => $CatOrDog], 'dogOrHuman' => ['type' => $DogOrHuman], 'humanOrAlien' => ['type' => $HumanOrAlien], 'complicatedArgs' => ['type' => $ComplicatedArgs]]]); $defaultSchema = new Schema($queryRoot); return $defaultSchema; }
/** * @it converts ID values to Int/String ASTs */ public function testConvertIdValuesToIntOrStringASTs() { $this->assertEquals(new StringValue(['value' => 'hello']), AST::astFromValue('hello', Type::id())); $this->assertEquals(new StringValue(['value' => 'VALUE']), AST::astFromValue('VALUE', Type::id())); $this->assertEquals(new StringValue(['value' => 'VA\\nLUE']), AST::astFromValue("VA\nLUE", Type::id())); $this->assertEquals(new IntValue(['value' => '123']), AST::astFromValue(123, Type::id())); $this->assertEquals(new StringValue(['value' => 'false']), AST::astFromValue(false, Type::id())); $this->assertEquals(null, AST::astFromValue(null, Type::id())); }
/** * @it does not convert when input coercion rules reject a value */ public function testDoesNotConvertWhenInputCoercionRulesRejectAValue() { $undefined = Utils::undefined(); $this->runTestCase(Type::boolean(), '123', $undefined); $this->runTestCase(Type::int(), '123.456', $undefined); $this->runTestCase(Type::int(), 'true', $undefined); $this->runTestCase(Type::int(), '"123"', $undefined); $this->runTestCase(Type::float(), '"123"', $undefined); $this->runTestCase(Type::string(), '123', $undefined); $this->runTestCase(Type::string(), 'true', $undefined); $this->runTestCase(Type::id(), '123.456', $undefined); }
public function testExecutesUsingASchema() { $BlogArticle = null; $BlogImage = new ObjectType(['name' => 'Image', 'fields' => ['url' => ['type' => Type::string()], 'width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]]]); $BlogAuthor = new ObjectType(['name' => 'Author', 'fields' => ['id' => ['type' => Type::string()], 'name' => ['type' => Type::string()], 'pic' => ['args' => ['width' => ['type' => Type::int()], 'height' => ['type' => Type::int()]], 'type' => $BlogImage, 'resolve' => function ($obj, $args) { return $obj['pic']($args['width'], $args['height']); }], 'recentArticle' => ['type' => function () use(&$BlogArticle) { return $BlogArticle; }]]]); $BlogArticle = new ObjectType(['name' => 'Article', 'fields' => ['id' => ['type' => Type::nonNull(Type::string())], 'isPublished' => ['type' => Type::boolean()], 'author' => ['type' => $BlogAuthor], 'title' => ['type' => Type::string()], 'body' => ['type' => Type::string()], 'keywords' => ['type' => Type::listOf(Type::string())]]]); $BlogQuery = new ObjectType(['name' => 'Query', 'fields' => ['article' => ['type' => $BlogArticle, 'args' => ['id' => ['type' => Type::id()]], 'resolve' => function ($_, $args) { return $this->article($args['id']); }], 'feed' => ['type' => Type::listOf($BlogArticle), 'resolve' => function () { return [$this->article(1), $this->article(2), $this->article(3), $this->article(4), $this->article(5), $this->article(6), $this->article(7), $this->article(8), $this->article(9), $this->article(10)]; }]]]); $BlogSchema = new Schema($BlogQuery); $request = ' { feed { id, title }, article(id: "1") { ...articleFields, author { id, name, pic(width: 640, height: 480) { url, width, height }, recentArticle { ...articleFields, keywords } } } } fragment articleFields on Article { id, isPublished, title, body, hidden, notdefined } '; $expected = ['data' => ['feed' => [['id' => '1', 'title' => 'My Article 1'], ['id' => '2', 'title' => 'My Article 2'], ['id' => '3', 'title' => 'My Article 3'], ['id' => '4', 'title' => 'My Article 4'], ['id' => '5', 'title' => 'My Article 5'], ['id' => '6', 'title' => 'My Article 6'], ['id' => '7', 'title' => 'My Article 7'], ['id' => '8', 'title' => 'My Article 8'], ['id' => '9', 'title' => 'My Article 9'], ['id' => '10', 'title' => 'My Article 10']], 'article' => ['id' => '1', 'isPublished' => true, 'title' => 'My Article 1', 'body' => 'This is a post', 'author' => ['id' => '123', 'name' => 'John Smith', 'pic' => ['url' => 'cdn://123', 'width' => 640, 'height' => 480], 'recentArticle' => ['id' => '1', 'isPublished' => true, 'title' => 'My Article 1', 'body' => 'This is a post', 'keywords' => ['foo', 'bar', '1', 'true', null]]]]]]; $this->assertEquals($expected, Executor::execute($BlogSchema, Parser::parse($request))->toArray()); }
/** * Arguments available on node query. * * @return array */ public function args() { return ['id' => ['name' => 'id', 'type' => Type::nonNull(Type::id())]]; }
/** * Initialize Alambic types using GraphQL scalar types. */ protected function initAlambicBaseTypes() { $this->alambicTypes = ['String' => Type::string(), 'Int' => Type::int(), 'Float' => Type::float(), 'Boolean' => Type::boolean(), 'ID' => Type::id()]; $this->inputAlambicTypes = ['String' => Type::string(), 'Int' => Type::int(), 'Float' => Type::float(), 'Boolean' => Type::boolean(), 'ID' => Type::id()]; }
/** * ID field. * * @param array|string $config * @return array */ public function id($config = []) { $description = is_string($config) ? $config : ''; $config = is_array($config) ? $config : []; return array_merge(['type' => Type::id(), 'description' => $description], $config); }
private function getTestSchema() { $StringBox = null; $IntBox = null; $SomeBox = null; $SomeBox = new InterfaceType(['name' => 'SomeBox', 'resolveType' => function () use(&$StringBox) { return $StringBox; }, 'fields' => function () use(&$SomeBox) { return ['deepBox' => ['type' => $SomeBox], 'unrelatedField' => ['type' => Type::string()]]; }]); $StringBox = new ObjectType(['name' => 'StringBox', 'interfaces' => [$SomeBox], 'fields' => function () use(&$StringBox, &$IntBox) { return ['scalar' => ['type' => Type::string()], 'deepBox' => ['type' => $StringBox], 'unrelatedField' => ['type' => Type::string()], 'listStringBox' => ['type' => Type::listOf($StringBox)], 'stringBox' => ['type' => $StringBox], 'intBox' => ['type' => $IntBox]]; }]); $IntBox = new ObjectType(['name' => 'IntBox', 'interfaces' => [$SomeBox], 'fields' => function () use(&$StringBox, &$IntBox) { return ['scalar' => ['type' => Type::int()], 'deepBox' => ['type' => $IntBox], 'unrelatedField' => ['type' => Type::string()], 'listStringBox' => ['type' => Type::listOf($StringBox)], 'stringBox' => ['type' => $StringBox], 'intBox' => ['type' => $IntBox]]; }]); $NonNullStringBox1 = new InterfaceType(['name' => 'NonNullStringBox1', 'resolveType' => function () use(&$StringBox) { return $StringBox; }, 'fields' => ['scalar' => ['type' => Type::nonNull(Type::string())]]]); $NonNullStringBox1Impl = new ObjectType(['name' => 'NonNullStringBox1Impl', 'interfaces' => [$SomeBox, $NonNullStringBox1], 'fields' => ['scalar' => ['type' => Type::nonNull(Type::string())], 'unrelatedField' => ['type' => Type::string()], 'deepBox' => ['type' => $SomeBox]]]); $NonNullStringBox2 = new InterfaceType(['name' => 'NonNullStringBox2', 'resolveType' => function () use(&$StringBox) { return $StringBox; }, 'fields' => ['scalar' => ['type' => Type::nonNull(Type::string())]]]); $NonNullStringBox2Impl = new ObjectType(['name' => 'NonNullStringBox2Impl', 'interfaces' => [$SomeBox, $NonNullStringBox2], 'fields' => ['scalar' => ['type' => Type::nonNull(Type::string())], 'unrelatedField' => ['type' => Type::string()], 'deepBox' => ['type' => $SomeBox]]]); $Connection = new ObjectType(['name' => 'Connection', 'fields' => ['edges' => ['type' => Type::listOf(new ObjectType(['name' => 'Edge', 'fields' => ['node' => ['type' => new ObjectType(['name' => 'Node', 'fields' => ['id' => ['type' => Type::id()], 'name' => ['type' => Type::string()]]])]]]))]]]); $schema = new Schema(['query' => new ObjectType(['name' => 'QueryRoot', 'fields' => ['someBox' => ['type' => $SomeBox], 'connection' => ['type' => $Connection]]]), 'types' => [$IntBox, $StringBox, $NonNullStringBox1Impl, $NonNullStringBox2Impl]]); return $schema; }
/** * Relay global identifier field. * * @return array */ protected function getRelayIdField() { return ['id' => ['type' => Type::nonNull(Type::id()), 'description' => 'ID of type.', 'resolve' => function ($obj) { return $this->encodeGlobalId(get_called_class(), $this->getIdentifier($obj)); }]]; }
/** * This will return a GraphQLFieldConfig for our ship * mutation. * * It creates these two types implicitly: * input IntroduceShipInput { * clientMutationId: string! * shipName: string! * factionId: ID! * } * * input IntroduceShipPayload { * clientMutationId: string! * ship: Ship * faction: Faction * } */ public static function getShipMutation() { if (self::$shipMutation === null) { $shipType = self::getShipType(); $factionType = self::getFactionType(); $shipMutation = Relay::mutationWithClientMutationId(['name' => 'IntroduceShip', 'inputFields' => ['shipName' => ['type' => Type::nonNull(Type::string())], 'factionId' => ['type' => Type::nonNull(Type::id())]], 'outputFields' => ['ship' => ['type' => $shipType, 'resolve' => function ($payload) { return StarWarsData::getShip($payload['shipId']); }], 'faction' => ['type' => $factionType, 'resolve' => function ($payload) { return StarWarsData::getFaction($payload['factionId']); }]], 'mutateAndGetPayload' => function ($input) { $newShip = StarWarsData::createShip($input['shipName'], $input['factionId']); return ['shipId' => $newShip['id'], 'factionId' => $input['factionId']]; }]); self::$shipMutation = $shipMutation; } return self::$shipMutation; }
/** * Creates the configuration for an id field on a node, using `self::toGlobalId` to * construct the ID from the provided typename. The type-specific ID is fetched * by calling idFetcher on the object, or if not provided, by accessing the `id` * property on the object. * * @param string|null $typeName * @param callable|null $idFetcher * @return array */ public static function globalIdField($typeName = null, callable $idFetcher = null) { return ['name' => 'id', 'description' => 'The ID of an object', 'type' => Type::nonNull(Type::id()), 'resolve' => function ($obj, $args, $context, $info) use($typeName, $idFetcher) { return self::toGlobalId($typeName !== null ? $typeName : $info->parentType->name, $idFetcher ? $idFetcher($obj, $info) : $obj['id']); }]; }
/** * Available fields on type. * * @return array */ public function fields() { return ['id' => ['type' => Type::nonNull(Type::id()), 'description' => 'The id of the object.']]; }
/** * @return \GraphQL\Type\Definition\IDType */ public static function id() { return Type::id(); }
/** * Returns node definitions * * @return array */ protected function getNodeDefinitions() { if (!self::$nodeDefinition) { self::$nodeDefinition = Node::nodeDefinitions(function ($id, $context, ResolveInfo $info) { $userData = $this->getUserData(); if (array_key_exists($id, $userData)) { return $userData[$id]; } else { $photoData = $this->getPhotoData(); if (array_key_exists($id, $photoData)) { return $photoData[$id]; } } }, function ($obj) { if (array_key_exists($obj['id'], $this->getUserData())) { return self::$userType; } else { return self::$photoType; } }); self::$userType = new ObjectType(['name' => 'User', 'fields' => ['id' => ['type' => Type::nonNull(Type::id())], 'name' => ['type' => Type::string()]], 'interfaces' => [self::$nodeDefinition['nodeInterface']]]); self::$photoType = new ObjectType(['name' => 'Photo', 'fields' => ['id' => ['type' => Type::nonNull(Type::id())], 'width' => ['type' => Type::int()]], 'interfaces' => [self::$nodeDefinition['nodeInterface']]]); } return self::$nodeDefinition; }
/** * Get IDType instance. * * @return \GraphQL\Type\Definition\IDType */ public function getIDType() { return Type::id(); }
/** * List of fields with global identifier. * * @return array */ public function fields() { return array_merge($this->relayFields(), $this->getConnections(), ['id' => ['type' => Type::nonNull(Type::id()), 'description' => 'ID of type.', 'resolve' => function ($obj) { return $this->encodeGlobalId(lcfirst($this->attributes['name']), $this->getIdentifier($obj)); }]]); }
/** * @see https://github.com/webonyx/graphql-php/issues/59 */ public function testSerializesToEmptyObjectVsEmptyArray() { $iface = null; $a = new ObjectType(['name' => 'A', 'fields' => ['id' => Type::id()], 'interfaces' => function () use(&$iface) { return [$iface]; }]); $b = new ObjectType(['name' => 'B', 'fields' => ['id' => Type::id()], 'interfaces' => function () use(&$iface) { return [$iface]; }]); $iface = new InterfaceType(['name' => 'Iface', 'fields' => ['id' => Type::id()], 'resolveType' => function ($v) use($a, $b) { return $v['type'] === 'A' ? $a : $b; }]); $schema = new Schema(['query' => new ObjectType(['name' => 'Query', 'fields' => ['ab' => Type::listOf($iface)]]), 'types' => [$a, $b]]); $data = ['ab' => [['id' => 1, 'type' => 'A'], ['id' => 2, 'type' => 'A'], ['id' => 3, 'type' => 'B'], ['id' => 4, 'type' => 'B']]]; $query = Parser::parse(' { ab { ... on A{ id } } } '); $result = Executor::execute($schema, $query, $data, null); $this->assertEquals(['data' => ['ab' => [['id' => '1'], ['id' => '2'], new \stdClass(), new \stdClass()]]], $result->toArray()); }
private function getTestSchema() { $StringBox = new ObjectType(['name' => 'StringBox', 'fields' => ['scalar' => ['type' => Type::string()]]]); $IntBox = new ObjectType(['name' => 'IntBox', 'fields' => ['scalar' => ['type' => Type::int()]]]); $NonNullStringBox1 = new ObjectType(['name' => 'NonNullStringBox1', 'fields' => ['scalar' => ['type' => Type::nonNull(Type::string())]]]); $NonNullStringBox2 = new ObjectType(['name' => 'NonNullStringBox2', 'fields' => ['scalar' => ['type' => Type::nonNull(Type::string())]]]); $BoxUnion = new UnionType(['name' => 'BoxUnion', 'resolveType' => function () use($StringBox) { return $StringBox; }, 'types' => [$StringBox, $IntBox, $NonNullStringBox1, $NonNullStringBox2]]); $Connection = new ObjectType(['name' => 'Connection', 'fields' => ['edges' => ['type' => Type::listOf(new ObjectType(['name' => 'Edge', 'fields' => ['node' => ['type' => new ObjectType(['name' => 'Node', 'fields' => ['id' => ['type' => Type::id()], 'name' => ['type' => Type::string()]]])]]]))]]]); $schema = new Schema(new ObjectType(['name' => 'QueryRoot', 'fields' => ['boxUnion' => ['type' => $BoxUnion], 'connection' => ['type' => $Connection]]])); return $schema; }
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)); }
/** * Resolve field type by column info. * * @param string $name * @param string $colType * @return \GraphQL\Type\Definition\Type */ protected function resolveTypeByColumn($name, $colType) { $type = Type::string(); $type->name = $this->getName() . '_String'; if ($name === $this->model->getKeyName()) { $type = Type::id(); $type->name = $this->getName() . '_ID'; } elseif ($colType === 'integer') { $type = Type::int(); $type->name = $this->getName() . '_Int'; } elseif ($colType === 'float' || $colType === 'decimal') { $type = Type::float(); $type->name = $this->getName() . '_Float'; } elseif ($colType === 'boolean') { $type = Type::boolean(); $type->name = $this->getName() . '_Boolean'; } return $type; }