'Named', 'fields' => [ 'name' => ['type' => Type::string()], ], ]); $DogType = new ObjectType([ 'name' => 'Dog', 'interfaces' => [$NamedType], 'fields' => [ 'name' => ['type' => Type::string()], 'woofs' => ['type' => Type::boolean()], ], 'isTypeOf' => function ($value) { return $value instanceof Dog; }, ]); $CatType = new ObjectType([ 'name' => 'Cat', 'interfaces' => [$NamedType], 'fields' => [ 'name' => ['type' => Type::string()], 'meows' => ['type' => Type::boolean()], ], 'isTypeOf' => function ($value) { return $value instanceof Cat; }, ]); $PetType = new UnionType([ 'name' => 'Pet', 'types' => [$DogType, $CatType], 'resolveType' => function ($value) use ($DogType, $CatType) { if ($value instanceof Dog) { return $DogType; } if ($value instanceof Cat) { return $CatType; } }, ]); $PersonType = new ObjectType([ 'name' => 'Person', 'interfaces' => [$NamedType], 'fields' => [ 'name' => ['type' => Type::string()], 'pets' => ['type' => Type::listOf($PetType)], 'friends' => ['type' => Type::listOf($NamedType)], ], 'isTypeOf' => function ($value) { return $value instanceof Person; }, ]); $this->schema = new Schema([ 'query' => $PersonType, 'types' => [$PetType], ]); $this->garfield = new Cat('Garfield', false); $this->odie = new Dog('Odie', true); $this->liz = new Person('Liz'); $this->john = new Person('John', [$this->garfield, $this->odie], [$this->liz, $this->odie]); } // Execute: Union and intersection types /** * @see it('can introspect on union and intersection types') */ public function testCanIntrospectOnUnionAndIntersectionTypes() : void { $ast = Parser::parse(' { Named: __type(name: "Named") { kind name fields { name } interfaces { name } possibleTypes { name } enumValues { name } inputFields { name } } Pet: __type(name: "Pet") { kind name fields { name } interfaces { name } possibleTypes { name } enumValues { name } inputFields { name } } } '); $expected = [ 'data' => [ 'Named' => [ 'kind' => 'INTERFACE', 'name' => 'Named', 'fields' => [ ['name' => 'name'], ], 'interfaces' => null, 'possibleTypes' => [ ['name' => 'Person'], ['name' => 'Dog'], ['name' => 'Cat'], ], 'enumValues' => null, 'inputFields' => null, ], 'Pet' => [ 'kind' => 'UNION', 'name' => 'Pet', 'fields' => null, 'interfaces' => null, 'possibleTypes' => [ ['name' => 'Dog'], ['name' => 'Cat'], ], 'enumValues' => null, 'inputFields' => null, ], ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast)->toArray()); } /** * @see it('executes using union types') */ public function testExecutesUsingUnionTypes() : void { // NOTE: This is an *invalid* query, but it should be an *executable* query. $ast = Parser::parse(' { __typename name pets { __typename name woofs meows } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], ], ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); } /** * @see it('executes union types with inline fragments') */ public function testExecutesUnionTypesWithInlineFragments() : void { // This is the valid version of the query in the above test. $ast = Parser::parse(' { __typename name pets { __typename ... on Dog { name woofs } ... on Cat { name meows } } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], ], ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); } /** * @see it('executes using interface types') */ public function testExecutesUsingInterfaceTypes() : void { // NOTE: This is an *invalid* query, but it should be an *executable* query. $ast = Parser::parse(' { __typename name friends { __typename name woofs meows } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], ], ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); } /** * @see it('executes interface types with inline fragments') */ public function testExecutesInterfaceTypesWithInlineFragments() : void { // This is the valid version of the query in the above test. $ast = Parser::parse(' { __typename name friends { __typename name ... on Dog { woofs } ... on Cat { meows } } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], ], ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray(true)); } /** * @see it('allows fragment conditions to be abstract types') */ public function testAllowsFragmentConditionsToBeAbstractTypes() : void { $ast = Parser::parse(' { __typename name pets { ...PetFields } friends { ...FriendFields } } fragment PetFields on Pet { __typename ... on Dog { name woofs } ... on Cat { name meows } } fragment FriendFields on Named { __typename name ... on Dog { woofs } ... on Cat { meows } } '); $expected = [ 'data' => [ '__typename' => 'Person', 'name' => 'John', 'pets' => [ ['__typename' => 'Cat', 'name' => 'Garfield', 'meows' => false], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], ], 'friends' => [ ['__typename' => 'Person', 'name' => 'Liz'], ['__typename' => 'Dog', 'name' => 'Odie', 'woofs' => true], ], ], ]; $this->assertEquals($expected, Executor::execute($this->schema, $ast, $this->john)->toArray()); } /** * @see it('gets execution info in resolver') */ public function testGetsExecutionInfoInResolver() : void { $encounteredContext = null; $encounteredSchema = null; $encounteredRootValue = null; $PersonType2 = null; $NamedType2 = new InterfaceType([ 'name' => 'Named', 'fields' => [ 'name' => ['type' => Type::string()], ], 'resolveType' => function ( $obj, $context, ResolveInfo $info ) use ( &$encounteredContext, & $encounteredSchema, &$encounteredRootValue, &$PersonType2 ) { $encounteredContext = $context; $encounteredSchema = $info->schema; $encounteredRootValue = $info->rootValue; return $PersonType2; }, ]); $PersonType2 = new ObjectType([ 'name' => 'Person', 'interfaces' => [$NamedType2], 'fields' => [ 'name' => ['type' => Type::string()], 'friends' => ['type' => Type::listOf($NamedType2)], ], ]); $schema2 = new Schema(['query' => $PersonType2]); $john2 = new Person('John', [], [$this->liz]); $context = ['authToken' => '123abc']; $ast = Parser::parse('{ name, friends { name } }'); $this->assertEquals( ['data' => ['name' => 'John', 'friends' => [['name' => 'Liz']]]], GraphQL::executeQuery($schema2, $ast, $john2, $context)->toArray() ); $this->assertSame($context, $encounteredContext); $this->assertSame($schema2, $encounteredSchema); $this->assertSame($john2, $encounteredRootValue); } }