Skip to content

Commit 86436f3

Browse files
committed
Add support for mutations
1 parent 17d9a75 commit 86436f3

File tree

6 files changed

+277
-12
lines changed

6 files changed

+277
-12
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
<?php
2+
3+
namespace GraphQL\SchemaGenerator\CodeGenerator;
4+
5+
use GraphQL\Enumeration\FieldTypeKindEnum;
6+
use GraphQL\SchemaGenerator\CodeGenerator\CodeFile\ClassFile;
7+
use GraphQL\SchemaObject\QueryObject;
8+
use GraphQL\Util\StringLiteralFormatter;
9+
10+
/**
11+
* Class QueryObjectClassBuilder
12+
*
13+
* @package GraphQL\SchemaManager\CodeGenerator
14+
*/
15+
class MutationObjectClassBuilder extends ObjectClassBuilder
16+
{
17+
/**
18+
* QueryObjectClassBuilder constructor.
19+
*
20+
* @param string $writeDir
21+
* @param string $objectName
22+
* @param string $namespace
23+
*/
24+
public function __construct(string $writeDir, string $objectName, string $namespace = self::DEFAULT_NAMESPACE)
25+
{
26+
$className = $objectName . 'MutationObject';
27+
28+
$this->classFile = new ClassFile($writeDir, $className);
29+
$this->classFile->setNamespace($namespace);
30+
if ($namespace !== self::DEFAULT_NAMESPACE) {
31+
$this->classFile->addImport('GraphQL\\SchemaObject\\MutationObject');
32+
}
33+
$this->classFile->extendsClass('MutationObject');
34+
35+
// Special case for handling root query object
36+
if ($objectName === QueryObject::ROOT_QUERY_OBJECT_NAME) {
37+
$objectName = '';
38+
}
39+
$this->classFile->addConstant('OBJECT_NAME', $objectName);
40+
}
41+
42+
/**
43+
* @param string $fieldName
44+
*/
45+
public function addScalarField(string $fieldName, bool $isDeprecated, ?string $deprecationReason)
46+
{
47+
$upperCamelCaseProp = StringLiteralFormatter::formatUpperCamelCase($fieldName);
48+
$this->addSimpleSelector($fieldName, $upperCamelCaseProp, $isDeprecated, $deprecationReason);
49+
}
50+
51+
/**
52+
* @param string $fieldName
53+
* @param string $typeName
54+
* @param string $typeKind
55+
* @param string|null $argsObjectName
56+
* @param bool $isDeprecated
57+
* @param string|null $deprecationReason
58+
*/
59+
public function addObjectField(string $fieldName, string $typeName, string $typeKind, ?string $argsObjectName, bool $isDeprecated, ?string $deprecationReason)
60+
{
61+
$upperCamelCaseProp = StringLiteralFormatter::formatUpperCamelCase($fieldName);
62+
$this->addObjectSelector($fieldName, $upperCamelCaseProp, $typeName, $typeKind, $argsObjectName, $isDeprecated, $deprecationReason);
63+
}
64+
65+
/**
66+
* @param string $propertyName
67+
* @param string $upperCamelName
68+
* @param bool $isDeprecated
69+
* @param string|null $deprecationReason
70+
*/
71+
protected function addSimpleSelector(string $propertyName, string $upperCamelName, bool $isDeprecated, ?string $deprecationReason)
72+
{
73+
$method = "public function select$upperCamelName()
74+
{
75+
\$this->selectField(\"$propertyName\");
76+
77+
return \$this;
78+
}";
79+
$this->classFile->addMethod($method, $isDeprecated, $deprecationReason);
80+
}
81+
82+
/**
83+
* @param string $fieldName
84+
* @param string $upperCamelName
85+
* @param string $fieldTypeName
86+
* @param string $fieldTypeKind
87+
* @param string|null $argsObjectName
88+
* @param bool $isDeprecated
89+
* @param string|null $deprecationReason
90+
*/
91+
protected function addObjectSelector(string $fieldName, string $upperCamelName, string $fieldTypeName, string $fieldTypeKind, ?string $argsObjectName, bool $isDeprecated, ?string $deprecationReason)
92+
{
93+
$objectClass = $fieldTypeName . ($fieldTypeKind === FieldTypeKindEnum::UNION_OBJECT ? 'UnionObject' : 'QueryObject');
94+
95+
if ($argsObjectName === null) {
96+
$method = "public function select$upperCamelName()
97+
{
98+
\$object = new $objectClass(\"$fieldName\");
99+
\$this->selectField(\$object);
100+
101+
return \$object;
102+
}";
103+
} else {
104+
$method = "public function select$upperCamelName($argsObjectName \$argsObject = null)
105+
{
106+
\$object = new $objectClass(\"$fieldName\");
107+
if (\$argsObject !== null) {
108+
\$object->appendArguments(\$argsObject->toArray());
109+
}
110+
\$this->selectField(\$object);
111+
112+
return \$object;
113+
}";
114+
}
115+
116+
$this->classFile->addMethod($method, $isDeprecated, $deprecationReason);
117+
}
118+
119+
/**
120+
* This method builds the class and writes it to the file system
121+
*/
122+
public function build(): void
123+
{
124+
$this->classFile->writeFile();
125+
}
126+
}

src/SchemaGenerator/SchemaClassGenerator.php

+38-8
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
use GraphQL\SchemaGenerator\CodeGenerator\EnumObjectBuilder;
99
use GraphQL\SchemaGenerator\CodeGenerator\InputObjectClassBuilder;
1010
use GraphQL\SchemaGenerator\CodeGenerator\InterfaceObjectBuilder;
11+
use GraphQL\SchemaGenerator\CodeGenerator\MutationObjectClassBuilder;
1112
use GraphQL\SchemaGenerator\CodeGenerator\ObjectBuilderInterface;
13+
use GraphQL\SchemaGenerator\CodeGenerator\ObjectClassBuilder;
1214
use GraphQL\SchemaGenerator\CodeGenerator\QueryObjectClassBuilder;
1315
use GraphQL\SchemaGenerator\CodeGenerator\UnionObjectBuilder;
16+
use GraphQL\SchemaObject\MutationObject;
1417
use GraphQL\SchemaObject\QueryObject;
1518
use GraphQL\Util\StringLiteralFormatter;
1619
use RuntimeException;
@@ -63,15 +66,20 @@ public function __construct(Client $client, string $writeDir = '', string $names
6366
$this->setWriteDir();
6467
}
6568

69+
public function generateRootObjects(): bool
70+
{
71+
$this->generateRootQueryObject();
72+
$this->generateRootMutationObject();
73+
}
74+
6675
/**
6776
* @return bool
6877
*/
69-
public function generateRootQueryObject(): bool
70-
{
71-
$objectArray = $this->schemaInspector->getQueryTypeSchema();
78+
public function generateRootQueryObject(): bool
79+
{
80+
$objectArray = $this->schemaInspector->getRootSchema('query');
7281
$rootObjectName = QueryObject::ROOT_QUERY_OBJECT_NAME;
7382
$queryTypeName = $objectArray['name'];
74-
//$rootObjectDescr = $objectArray['description'];
7583

7684
if (array_key_exists($queryTypeName, $this->generatedObjects)) {
7785
return true;
@@ -86,14 +94,36 @@ public function generateRootQueryObject(): bool
8694
return true;
8795
}
8896

97+
/**
98+
* @return bool
99+
*/
100+
public function generateRootMutationObject(): bool
101+
{
102+
$objectArray = $this->schemaInspector->getRootSchema('mutation');
103+
$rootObjectName = MutationObject::ROOT_MUTATION_OBJECT_NAME;
104+
$queryTypeName = $objectArray['name'];
105+
106+
if (array_key_exists($queryTypeName, $this->generatedObjects)) {
107+
return true;
108+
}
109+
110+
$this->generatedObjects[$queryTypeName] = true;
111+
112+
$queryObjectBuilder = new MutationObjectClassBuilder($this->writeDir, $rootObjectName, $this->generationNamespace);
113+
$this->appendQueryObjectFields($queryObjectBuilder, $rootObjectName, $objectArray['fields']);
114+
$queryObjectBuilder->build();
115+
116+
return true;
117+
}
118+
89119
/**
90120
* This method receives the array of object fields as an input and adds the fields to the query object building
91121
*
92-
* @param QueryObjectClassBuilder $queryObjectBuilder
93-
* @param string $currentTypeName
94-
* @param array $fieldsArray
122+
* @param ObjectClassBuilder $queryObjectBuilder
123+
* @param string $currentTypeName
124+
* @param array $fieldsArray
95125
*/
96-
private function appendQueryObjectFields(QueryObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray)
126+
private function appendQueryObjectFields(ObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray)
97127
{
98128
foreach ($fieldsArray as $fieldArray) {
99129
$name = $fieldArray['name'];

src/SchemaGenerator/SchemaInspector.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@ public function __construct(Client $client)
5454
}
5555

5656
/**
57+
* @param string $type query or mutation
5758
* @return array
5859
*/
59-
public function getQueryTypeSchema(): array
60+
public function getRootSchema(string $type): array
6061
{
6162
$schemaQuery = "{
6263
__schema{
63-
queryType{
64+
${type}Type{
6465
name
6566
kind
6667
description
@@ -82,7 +83,7 @@ public function getQueryTypeSchema(): array
8283
}";
8384
$response = $this->client->runRawQuery($schemaQuery, true);
8485

85-
return $response->getData()['__schema']['queryType'];
86+
return $response->getData()['__schema'][$type.'Type'];
8687
}
8788

8889
/**

src/SchemaObject/MutationObject.php

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace GraphQL\SchemaObject;
4+
5+
use GraphQL\Query;
6+
use GraphQL\QueryBuilder\AbstractQueryBuilder;
7+
8+
/**
9+
* An abstract class that acts as the base for all schema query objects generated by the SchemaClassGenerator
10+
*
11+
* Class QueryObject
12+
*
13+
* @package GraphQL\SchemaObject
14+
*/
15+
abstract class MutationObject extends AbstractQueryBuilder
16+
{
17+
/**
18+
* This constant stores the name to be given to the root mutation object
19+
*
20+
* @var string
21+
*/
22+
public const ROOT_MUTATION_OBJECT_NAME = 'Root';
23+
24+
/**
25+
* This constant stores the name of the object name in the API definition
26+
*
27+
* @var string
28+
*/
29+
protected const OBJECT_NAME = '';
30+
31+
/**
32+
* SchemaObject constructor.
33+
*
34+
* @param string $fieldName
35+
*/
36+
public function __construct(string $fieldName = '')
37+
{
38+
$queryObject = !empty($fieldName) ? $fieldName : static::OBJECT_NAME;
39+
parent::__construct($queryObject);
40+
}
41+
42+
/**
43+
* @param array $arguments
44+
*
45+
* @return $this
46+
*/
47+
protected function appendArguments(array $arguments): MutationObject
48+
{
49+
foreach ($arguments as $argName => $argValue) {
50+
if ($argValue instanceof InputObject) {
51+
$argValue = $argValue->toRawObject();
52+
}
53+
54+
$this->setArgument($argName, $argValue);
55+
}
56+
57+
return $this;
58+
}
59+
60+
/**
61+
* @return Query
62+
*/
63+
public function getQuery(): Query
64+
{
65+
return parent::getQuery();
66+
}
67+
}

tests/SchemaClassGeneratorTest.php

+32-1
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,7 @@ public function testGenerateQueryObjectWithObjectFields()
790790
/**
791791
* @covers \GraphQL\SchemaGenerator\SchemaClassGenerator::generateRootQueryObject
792792
*/
793-
public function testGenerateRootObject()
793+
public function testGenerateRootQueryObject()
794794
{
795795
$this->mockHandler->append(new Response(200, [], json_encode([
796796
'data' => [
@@ -813,6 +813,32 @@ public function testGenerateRootObject()
813813
);
814814
}
815815

816+
/**
817+
* @covers \GraphQL\SchemaGenerator\SchemaClassGenerator::generateRootQueryObject
818+
*/
819+
public function testGenerateRootMutationObject()
820+
{
821+
$this->mockHandler->append(new Response(200, [], json_encode([
822+
'data' => [
823+
'__schema' => [
824+
'mutationType' => [
825+
'name' => 'Mutation',
826+
'kind' => FieldTypeKindEnum::OBJECT,
827+
'description' => null,
828+
'fields' => []
829+
]
830+
]
831+
]
832+
])));
833+
$this->classGenerator->generateRootMutationObject();
834+
835+
$objectName = 'RootMutationObject';
836+
$this->assertFileEquals(
837+
static::getExpectedFilesDir() . "/mutation_objects/$objectName.php",
838+
static::getGeneratedFilesDir() . "/$objectName.php"
839+
);
840+
}
841+
816842
/**
817843
* @covers \GraphQL\SchemaGenerator\SchemaClassGenerator::generateUnionObject
818844
*/
@@ -1010,6 +1036,11 @@ public function generateRootQueryObject(): bool
10101036
return parent::generateRootQueryObject();
10111037
}
10121038

1039+
public function generateRootMutationObject(): bool
1040+
{
1041+
return parent::generateRootMutationObject();
1042+
}
1043+
10131044
public function generateQueryObject(string $objectName): bool
10141045
{
10151046
return parent::generateQueryObject($objectName);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace GraphQL\Tests\SchemaObject;
4+
5+
use GraphQL\SchemaObject\MutationObject;
6+
7+
class RootMutationObject extends MutationObject
8+
{
9+
const OBJECT_NAME = "";
10+
}

0 commit comments

Comments
 (0)