diff --git a/src/Enumeration/FieldTypeKindEnum.php b/src/Enumeration/FieldTypeKindEnum.php index 28dfa7b..d70614a 100644 --- a/src/Enumeration/FieldTypeKindEnum.php +++ b/src/Enumeration/FieldTypeKindEnum.php @@ -9,11 +9,12 @@ */ class FieldTypeKindEnum { - const SCALAR = 'SCALAR'; - const LIST = 'LIST'; - const NON_NULL = 'NON_NULL'; - const OBJECT = 'OBJECT'; - const INPUT_OBJECT = 'INPUT_OBJECT'; - const ENUM_OBJECT = 'ENUM'; - const UNION_OBJECT = 'UNION'; + const SCALAR = 'SCALAR'; + const LIST = 'LIST'; + const NON_NULL = 'NON_NULL'; + const OBJECT = 'OBJECT'; + const INPUT_OBJECT = 'INPUT_OBJECT'; + const ENUM_OBJECT = 'ENUM'; + const UNION_OBJECT = 'UNION'; + const INTERFACE_OBJECT = 'INTERFACE'; } diff --git a/src/SchemaGenerator/CodeGenerator/InterfaceObjectClassBuilder.php b/src/SchemaGenerator/CodeGenerator/InterfaceObjectClassBuilder.php new file mode 100644 index 0000000..d71f017 --- /dev/null +++ b/src/SchemaGenerator/CodeGenerator/InterfaceObjectClassBuilder.php @@ -0,0 +1,127 @@ +classFile = new ClassFile($writeDir, $className); + $this->classFile->setNamespace($namespace); + if ($namespace !== self::DEFAULT_NAMESPACE) { + $this->classFile->addImport('GraphQL\\SchemaObject\\UnionObject'); + } + $this->classFile->extendsClass('UnionObject'); + + $this->classFile->addConstant('OBJECT_NAME', $objectName); + } + + /** + * @param string $fieldName + */ + public function addScalarField(string $fieldName, bool $isDeprecated, ?string $deprecationReason) + { + $upperCamelCaseProp = StringLiteralFormatter::formatUpperCamelCase($fieldName); + $this->addSimpleSelector($fieldName, $upperCamelCaseProp, $isDeprecated, $deprecationReason); + } + + /** + * @param string $fieldName + * @param string $typeName + * @param string $typeKind + * @param string $argsObjectName + * @param bool $isDeprecated + * @param string|null $deprecationReason + */ + public function addObjectField(string $fieldName, string $typeName, string $typeKind, string $argsObjectName, bool $isDeprecated, ?string $deprecationReason) + { + $upperCamelCaseProp = StringLiteralFormatter::formatUpperCamelCase($fieldName); + $this->addObjectSelector($fieldName, $upperCamelCaseProp, $typeName, $typeKind, $argsObjectName, $isDeprecated, $deprecationReason); + } + + /** + * @param string $propertyName + * @param string $upperCamelName + * @param bool $isDeprecated + * @param string|null $deprecationReason + */ + protected function addSimpleSelector(string $propertyName, string $upperCamelName, bool $isDeprecated, ?string $deprecationReason) + { + $method = "public function select$upperCamelName() +{ + \$this->selectField(\"$propertyName\"); + + return \$this; +}"; + $this->classFile->addMethod($method, $isDeprecated, $deprecationReason); + } + + /** + * @param string $fieldName + * @param string $upperCamelName + * @param string $fieldTypeName + * @param string $fieldTypeKind + * @param string $argsObjectName + * @param bool $isDeprecated + * @param string|null $deprecationReason + */ + protected function addObjectSelector(string $fieldName, string $upperCamelName, string $fieldTypeName, string $fieldTypeKind, string $argsObjectName, bool $isDeprecated, ?string $deprecationReason) + { + $objectClass = $fieldTypeName . ($fieldTypeKind === FieldTypeKindEnum::UNION_OBJECT ? 'UnionObject' : 'QueryObject'); + $method = "public function select$upperCamelName($argsObjectName \$argsObject = null) +{ + \$object = new $objectClass(\"$fieldName\"); + if (\$argsObject !== null) { + \$object->appendArguments(\$argsObject->toArray()); + } + \$this->selectField(\$object); + + return \$object; +}"; + $this->classFile->addMethod($method, $isDeprecated, $deprecationReason); + } + + + /** + * @param string $typeName + */ + public function addPossibleType(string $typeName) + { + $upperCamelCaseTypeName = StringLiteralFormatter::formatUpperCamelCase($typeName); + $objectClassName = $typeName . 'QueryObject'; + $method = "public function on$upperCamelCaseTypeName() +{ + \$object = new $objectClassName(); + \$this->addPossibleType(\$object); + + return \$object; +}"; + $this->classFile->addMethod($method); + } + + /** + * This method builds the class and writes it to the file system + */ + public function build(): void + { + $this->classFile->writeFile(); + } +} diff --git a/src/SchemaGenerator/CodeGenerator/QueryObjectClassBuilder.php b/src/SchemaGenerator/CodeGenerator/QueryObjectClassBuilder.php index a8f4d03..08bb55b 100644 --- a/src/SchemaGenerator/CodeGenerator/QueryObjectClassBuilder.php +++ b/src/SchemaGenerator/CodeGenerator/QueryObjectClassBuilder.php @@ -104,6 +104,23 @@ protected function addObjectSelector(string $fieldName, string $upperCamelName, $this->classFile->addMethod($method, $isDeprecated, $deprecationReason); } + /** + * @param string $typeName + */ + public function addPossibleType(string $typeName) + { + $upperCamelCaseTypeName = StringLiteralFormatter::formatUpperCamelCase($typeName); + $objectClassName = $typeName . 'QueryObject'; + $method = "public function on$upperCamelCaseTypeName() +{ + \$object = new $objectClassName(); + \$this->addPossibleType(\$object); + + return \$object; +}"; + $this->classFile->addMethod($method); + } + /** * This method builds the class and writes it to the file system */ diff --git a/src/SchemaGenerator/SchemaClassGenerator.php b/src/SchemaGenerator/SchemaClassGenerator.php index e4920ad..fc4a692 100644 --- a/src/SchemaGenerator/SchemaClassGenerator.php +++ b/src/SchemaGenerator/SchemaClassGenerator.php @@ -7,7 +7,9 @@ use GraphQL\SchemaGenerator\CodeGenerator\ArgumentsObjectClassBuilder; use GraphQL\SchemaGenerator\CodeGenerator\EnumObjectBuilder; use GraphQL\SchemaGenerator\CodeGenerator\InputObjectClassBuilder; +use GraphQL\SchemaGenerator\CodeGenerator\InterfaceObjectClassBuilder; use GraphQL\SchemaGenerator\CodeGenerator\ObjectBuilderInterface; +use GraphQL\SchemaGenerator\CodeGenerator\ObjectClassBuilder; use GraphQL\SchemaGenerator\CodeGenerator\QueryObjectClassBuilder; use GraphQL\SchemaGenerator\CodeGenerator\UnionObjectBuilder; use GraphQL\SchemaObject\QueryObject; @@ -88,11 +90,11 @@ public function generateRootQueryObject(): bool /** * This method receives the array of object fields as an input and adds the fields to the query object building * - * @param QueryObjectClassBuilder $queryObjectBuilder - * @param string $currentTypeName - * @param array $fieldsArray + * @param ObjectClassBuilder $queryObjectBuilder + * @param string $currentTypeName + * @param array $fieldsArray */ - private function appendQueryObjectFields(QueryObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray) + private function appendQueryObjectFields(ObjectClassBuilder $queryObjectBuilder, string $currentTypeName, array $fieldsArray) { foreach ($fieldsArray as $fieldArray) { $name = $fieldArray['name']; @@ -143,6 +145,8 @@ protected function generateObject(string $objectName, string $objectKind): bool return $this->generateEnumObject($objectName); case FieldTypeKindEnum::UNION_OBJECT: return $this->generateUnionObject($objectName); + case FieldTypeKindEnum::INTERFACE_OBJECT: + return $this->generateInterfaceObject($objectName); default: print "Couldn't generate type $objectName: generating $objectKind kind is not supported yet" . PHP_EOL; return false; @@ -269,6 +273,35 @@ protected function generateUnionObject(string $objectName): bool return true; } + /** + * @param string $objectName + * + * @return bool + */ + protected function generateInterfaceObject(string $objectName): bool + { + if (array_key_exists($objectName, $this->generatedObjects)) { + return true; + } + + $this->generatedObjects[$objectName] = true; + + $objectArray = $this->schemaInspector->getInterfaceObjectSchema($objectName); + $objectName = $objectArray['name']; + $objectBuilder = new InterfaceObjectClassBuilder($this->writeDir, $objectName, $this->generationNamespace); + + $this->appendQueryObjectFields($objectBuilder, $objectName, $objectArray['fields']); + + foreach ($objectArray['possibleTypes'] as $possibleType) { + $this->generateObject($possibleType['name'], $possibleType['kind']); + $objectBuilder->addPossibleType($possibleType['name']); + } + + $objectBuilder->build(); + + return true; + } + /** * @param string $argsObjectName * @param array $arguments diff --git a/src/SchemaGenerator/SchemaInspector.php b/src/SchemaGenerator/SchemaInspector.php index 424ccc7..7f5be71 100644 --- a/src/SchemaGenerator/SchemaInspector.php +++ b/src/SchemaGenerator/SchemaInspector.php @@ -183,4 +183,39 @@ public function getUnionObjectSchema(string $objectName): array return $response->getData()['__type']; } + + /** + * @param string $objectName + * + * @return array + */ + public function getInterfaceObjectSchema(string $objectName): array + { + $schemaQuery = "{ + __type(name: \"$objectName\") { + name + kind + fields(includeDeprecated: true){ + name + description + isDeprecated + deprecationReason + " . static::TYPE_SUB_QUERY . " + args{ + name + description + defaultValue + " . static::TYPE_SUB_QUERY . " + } + } + possibleTypes { + kind + name + } + } +}"; + $response = $this->client->runRawQuery($schemaQuery, true); + + return $response->getData()['__type']; + } } diff --git a/src/SchemaObject/UnionObject.php b/src/SchemaObject/UnionObject.php index e211ad1..705920a 100644 --- a/src/SchemaObject/UnionObject.php +++ b/src/SchemaObject/UnionObject.php @@ -3,7 +3,6 @@ namespace GraphQL\SchemaObject; use GraphQL\InlineFragment; -use GraphQL\Query; /** * Class UnionObject