diff --git a/src/php/ModelGenerator.php b/src/php/ModelGenerator.php index c2543d9e73cda809da616040a693dbe95b7a0e6e..bc0f02ebfc645dd28d221062f0908bb3861e5a3a 100644 --- a/src/php/ModelGenerator.php +++ b/src/php/ModelGenerator.php @@ -30,17 +30,11 @@ use Irstea\NgModelGeneratorBundle\Metadata\ResourceMetadata; use Irstea\NgModelGeneratorBundle\Models\ClassName; use Irstea\NgModelGeneratorBundle\Models\Declaration; use Irstea\NgModelGeneratorBundle\Models\PHPClass; -use Irstea\NgModelGeneratorBundle\Models\Types\Alias; -use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType; -use Irstea\NgModelGeneratorBundle\Models\Types\Objects\InterfaceType; +use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeFactoryInterface; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Repository; -use Irstea\NgModelGeneratorBundle\Models\Types\Resources\UUID; -use Irstea\NgModelGeneratorBundle\Models\Types\StringConst; use Irstea\NgModelGeneratorBundle\Models\Types\Type; -use Irstea\NgModelGeneratorBundle\Models\Types\Union; use Irstea\NgModelGeneratorBundle\Writers\MultiFileWriter; -use Symfony\Component\PropertyInfo\Type as PHPType; use Twig\Environment; /** @@ -57,24 +51,25 @@ final class ModelGenerator /** @var Documentation */ private $documentation; - /** @var TypeFactoryInterface */ - private $typeFactory; - /** @var SerializationMapperFactoryInterface */ private $serializationMapperFactory; /** * Serializer constructor. * - * @param MetadataFactoryInterface $metadataFactory - * @param Environment $twigEnv + * @param MetadataFactoryInterface $metadataFactory + * @param SerializationMapperFactoryInterface $serializationMapperFactory + * @param TypeFactoryInterface $typeFactory + * @param Environment $twigEnv */ public function __construct( MetadataFactoryInterface $metadataFactory, + SerializationMapperFactoryInterface $serializationMapperFactory, Environment $twigEnv ) { $this->metadataFactory = $metadataFactory; $this->twigEnv = $twigEnv; + $this->serializationMapperFactory = $serializationMapperFactory; } /** @@ -83,10 +78,6 @@ final class ModelGenerator * * @param Documentation $doc * @param MultiFileWriter $writer - * - * @throws \Twig_Error_Loader - * @throws \Twig_Error_Runtime - * @throws \Twig_Error_Syntax */ public function generate(Documentation $doc, MultiFileWriter $writer): void { @@ -107,17 +98,9 @@ final class ModelGenerator /** * @param MultiFileWriter $writer - * - * @throws \Twig_Error_Loader - * @throws \Twig_Error_Runtime - * @throws \Twig_Error_Syntax */ private function doGenerate(MultiFileWriter $writer): void { - $this->typeFactory = $this->createTypeFactory(); - - $this->serializationMapperFactory = new SerializationMapperFactory($this->typeFactory); - $context = [ 'title' => $this->documentation->getTitle(), 'version' => $this->documentation->getVersion(), @@ -167,10 +150,6 @@ final class ModelGenerator /** * @param MultiFileWriter $writer - * - * @throws \Twig_Error_Loader - * @throws \Twig_Error_Runtime - * @throws \Twig_Error_Syntax */ private function generateFile(MultiFileWriter $writer, string $path, array $context): void { @@ -185,72 +164,6 @@ final class ModelGenerator } } - /** - * Crée une usine à types contenant un certain nombre de types par défaut. - * - * @return TypeFactoryInterface - */ - private function createTypeFactory(): TypeFactoryInterface - { - $factory = new TypeFactory(); - - foreach (['Array', 'Date', 'boolean', 'number', 'null', 'string', 'any', 'never'] as $builtin) { - $factory->add($builtin, BuiltinType::get($builtin)); - } - - $factory->add('UUID', UUID::get()); - $factory->add('CommonFilters', $this->createCommonFilters('CommonFilters')); - $factory->add('Ordering', $this->createOrdering()); - - foreach ([ - PHPType::BUILTIN_TYPE_ARRAY => 'Array', - PHPType::BUILTIN_TYPE_BOOL => 'boolean', - PHPType::BUILTIN_TYPE_FLOAT => 'number', - PHPType::BUILTIN_TYPE_INT => 'number', - PHPType::BUILTIN_TYPE_NULL => 'null', - PHPType::BUILTIN_TYPE_STRING => 'string', - 'Ramsey\Uuid\UuidInterface' => 'UUID', - 'DateTime' => 'string', - 'DateTimeInterface' => 'DateTime', - 'DateTimeImmutable' => 'DateTime', - ] as $alias => $target) { - if ($target === $alias || $factory->has($alias)) { - continue; - } - $factory->add($alias, $factory->get($target)); - } - - return $factory; - } - - /** - * @param string $name - * - * @return Type - */ - private function createCommonFilters(string $name): Type - { - $properties = []; - - $meta = $this->metadataFactory->getPaginationMetadata(); - if ($meta->isEnabled()) { - $properties[] = new Property($meta->getPageParameterName(), '', BuiltinType::get('number'), false, true); - if ($meta->isClientItemsPerPage()) { - $properties[] = new Property($meta->getItemsPerPageParameterName(), '', BuiltinType::get('number'), false, true); - } - } - - return new InterfaceType($name, null, $properties); - } - - /** - * @return Type - */ - private function createOrdering(): Type - { - return new Alias('Ordering', Union::create([StringConst::get('asc'), StringConst::get('desc')]), 'Allowed values for ordering parameters'); - } - /** * Retourne un iterateur sur les métadonnées des ressources. * @@ -274,13 +187,13 @@ final class ModelGenerator { $repositories = []; - // Premier passage pour prégénérer des référénces aux ressources - foreach ($this->getResourceMetadata() as $class => $resourceMeta) { - /* @var ClassName $class */ - /* @var ResourceMetadata $resourceMeta */ - $ref = $this->typeFactory->defer($class->getFullName()); - $this->typeFactory->add($resourceMeta->getBaseName(), $ref); - } +// // Premier passage pour prégénérer des références aux ressources +// foreach ($this->getResourceMetadata() as $class => $resourceMeta) { +// /* @var ClassName $class */ +// /* @var ResourceMetadata $resourceMeta */ +// $ref = $this->typeFactory->defer($class->getFullName()); +// $this->typeFactory->add($resourceMeta->getBaseName(), $ref); +// } // Maintenant on génére les repositories foreach ($this->getResourceMetadata() as $class => $resourceMeta) { @@ -334,7 +247,7 @@ final class ModelGenerator "No identifier found for %s (groups: [%s])\n", $resourceMeta->getBaseName(), implode(', ', $defaultNormalization->getGroups()) - ); + ); return null; } diff --git a/src/php/Models/Types/Factory/AbstractTypeFactoryDecorator.php b/src/php/Models/Types/Factory/AbstractTypeFactoryDecorator.php new file mode 100644 index 0000000000000000000000000000000000000000..6bd6c84db710fa675774ac395630730c3f570755 --- /dev/null +++ b/src/php/Models/Types/Factory/AbstractTypeFactoryDecorator.php @@ -0,0 +1,62 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class AbstractTypeFactoryDecorator. + */ +abstract class AbstractTypeFactoryDecorator implements TypeFactoryInterface +{ + /** + * @var TypeFactoryInterface + */ + private $decorated; + + /** + * AbstractTypeFactoryDecorator constructor. + * + * @param TypeFactoryInterface $decorated + */ + public function __construct(TypeFactoryInterface $decorated) + { + $this->decorated = $decorated; + } + + /** + * {@inheritdoc} + */ + public function supportsType(PropertyType $type, ContextInterface $context): bool + { + return $this->decorated->supportsType($type, $context); + } + + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type, ContextInterface $context): Type + { + return $this->decorated->createType($type, $context); + } +} diff --git a/src/php/Models/Types/Factory/BuiltinTypeFactory.php b/src/php/Models/Types/Factory/BuiltinTypeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..fa27f52fe20539c09ba3254c4f235de18d998391 --- /dev/null +++ b/src/php/Models/Types/Factory/BuiltinTypeFactory.php @@ -0,0 +1,94 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Assert\Assertion; +use Irstea\NgModelGeneratorBundle\Models\Types\Alias; +use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType; +use Irstea\NgModelGeneratorBundle\Models\Types\Resources\UUID; +use Irstea\NgModelGeneratorBundle\Models\Types\StringConst; +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Irstea\NgModelGeneratorBundle\Models\Types\Union; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class BuiltinTypeFactory. + */ +class BuiltinTypeFactory implements TypeFactoryInterface +{ + /** + * @var array<string, Type> + */ + private $types = []; + + /** + * BuiltinTypeFactory constructor. + * + * @param array<string, Type> $types + */ + public function __construct(array $types) + { + Assertion::allIsInstanceOf($types, Type::class); + + $this->types = $types; + } + + /** + * {@inheritdoc} + */ + public function supportsType(PropertyType $type, ContextInterface $context): bool + { + return \array_key_exists(TypeHelper::getTypeKey($type), $this->types); + } + + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type, ContextInterface $context): Type + { + Assertion::true($this->supportsType($type, $context)); + + return $this->types[TypeHelper::getTypeKey($type)]; + } + + /** + * @return BuiltinTypeFactory + */ + public static function createDefault(): BuiltinTypeFactory + { + $types = [ + PropertyType::BUILTIN_TYPE_ARRAY => BuiltinType::get('Array'), + PropertyType::BUILTIN_TYPE_BOOL => BuiltinType::get('boolean'), + PropertyType::BUILTIN_TYPE_FLOAT => BuiltinType::get('number'), + PropertyType::BUILTIN_TYPE_INT => BuiltinType::get('number'), + PropertyType::BUILTIN_TYPE_NULL => BuiltinType::get('null'), + PropertyType::BUILTIN_TYPE_STRING => BuiltinType::get('string'), + 'Ramsey\Uuid\UuidInterface' => UUID::get(), + 'DateTime' => BuiltinType::get('string'), + 'DateTimeInterface' => BuiltinType::get('string'), + 'DateTimeImmutable' => BuiltinType::get('string'), + 'Ordering' => new Alias('Ordering', Union::create([StringConst::get('asc'), StringConst::get('desc')]), 'Allowed values for ordering parameters'), + ]; + + return new BuiltinTypeFactory($types); + } +} diff --git a/src/php/Models/Types/Factory/CollectionTypeFactory.php b/src/php/Models/Types/Factory/CollectionTypeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7e3ef073c7e8d6b514bc846ba504994c87b2fc39 --- /dev/null +++ b/src/php/Models/Types/Factory/CollectionTypeFactory.php @@ -0,0 +1,51 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Assert\Assertion; +use Irstea\NgModelGeneratorBundle\Models\Types\ArrayType; +use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType; +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class CollectionTypeFactory. + */ +final class CollectionTypeFactory +{ + public function supportsType(PropertyType $type, ContextInterface $context): bool + { + return $type->isCollection(); + } + + public function createType(PropertyType $type, ContextInterface $context): Type + { + Assertion::true($this->supportsType($type, $context)); + + $valueType = $type->getCollectionValueType(); + if (!$valueType) { + return BuiltinType::get('Array'); + } + + return new ArrayType($context->createType($valueType)); + } +} diff --git a/src/php/Models/Types/Factory/CompositeTypeFactory.php b/src/php/Models/Types/Factory/CompositeTypeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..681940d1f8bc8cefdb9ccfdfcbff4fde5d331f99 --- /dev/null +++ b/src/php/Models/Types/Factory/CompositeTypeFactory.php @@ -0,0 +1,77 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Assert\Assertion; +use Irstea\NgModelGeneratorBundle\Exceptions\TypeNotFoundException; +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class CompositeTypeFactory. + */ +final class CompositeTypeFactory implements TypeFactoryInterface +{ + /** + * @var TypeFactoryInterface[] + */ + private $typeFactories; + + /** + * CompositeTypeFactory constructor. + * + * @param array $typeFactories + */ + public function __construct(array $typeFactories) + { + Assertion::allIsInstanceOf($typeFactories, TypeFactoryInterface::class); + $this->typeFactories = $typeFactories; + } + + /** + * {@inheritdoc} + */ + public function supportsType(PropertyType $type, ContextInterface $context): bool + { + foreach ($this->typeFactories as $typeFactory) { + if ($typeFactory->supportsType($type, $context)) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type, ContextInterface $context): Type + { + foreach ($this->typeFactories as $typeFactory) { + if ($typeFactory->supportsType($type, $context)) { + return $typeFactory->createType($type, $context); + } + } + + throw new TypeNotFoundException(TypeHelper::getTypeKey($type)); + } +} diff --git a/src/php/Models/Types/Factory/Context.php b/src/php/Models/Types/Factory/Context.php new file mode 100644 index 0000000000000000000000000000000000000000..2bcdb0015b09abfed1c1b60e144a7aafeb747e8e --- /dev/null +++ b/src/php/Models/Types/Factory/Context.php @@ -0,0 +1,62 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class Context. + */ +final class Context implements ContextInterface +{ + /** + * @var TypeFactoryInterface + */ + private $typeFactory; + + /** + * Context constructor. + * + * @param TypeFactoryInterface $typeFactory + */ + public function __construct(TypeFactoryInterface $typeFactory) + { + $this->typeFactory = $typeFactory; + } + + /** + * {@inheritdoc} + */ + public function supportsType(PropertyType $type): bool + { + return $this->typeFactory->supportsType($type, $this); + } + + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type): Type + { + return $this->typeFactory->createType($type, $this); + } +} diff --git a/src/php/Models/Types/Factory/ContextInterface.php b/src/php/Models/Types/Factory/ContextInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..164a4ea96bddfd2a0986918108a4f482a9a5e78c --- /dev/null +++ b/src/php/Models/Types/Factory/ContextInterface.php @@ -0,0 +1,45 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class ContextInterface. + */ +interface ContextInterface +{ + /** + * @param PropertyType $type + * + * @return bool + */ + public function supportsType(PropertyType $type): bool; + + /** + * @param PropertyType $type + * + * @return Type + */ + public function createType(PropertyType $type): Type; +} diff --git a/src/php/Models/Types/Factory/DeferrableTypeFactory.php b/src/php/Models/Types/Factory/DeferrableTypeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..bd47ecc6290a3d48e4f68a456e21d225443d6295 --- /dev/null +++ b/src/php/Models/Types/Factory/DeferrableTypeFactory.php @@ -0,0 +1,42 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Irstea\NgModelGeneratorBundle\Models\Types\Reference; +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class DeferrableTypeFactory. + */ +final class DeferrableTypeFactory extends AbstractTypeFactoryDecorator +{ + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type, ContextInterface $context): Type + { + $type = new Reference(TypeHelper::getTypeKey($type)); + + return $type; + } +} diff --git a/src/php/Models/Types/Factory/NullableTypeFactory.php b/src/php/Models/Types/Factory/NullableTypeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..7b986abba973b92c05789c764ea14bd4f3f576dc --- /dev/null +++ b/src/php/Models/Types/Factory/NullableTypeFactory.php @@ -0,0 +1,52 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Assert\Assertion; +use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType; +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Irstea\NgModelGeneratorBundle\Models\Types\Union; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class NullableTypeFactory. + */ +final class NullableTypeFactory implements TypeFactoryInterface +{ + /** + * {@inheritdoc} + */ + public function supportsType(PropertyType $type, ContextInterface $context): bool + { + return $type->isNullable(); + } + + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type, ContextInterface $context): Type + { + Assertion::true($this->supportsType($type, $context)); + + return Union::create([BuiltinType::get('null'), $context->createType(TypeHelper::notNullable($type))]); + } +} diff --git a/src/php/Models/Types/Factory/ReflectionTypeFactory.php b/src/php/Models/Types/Factory/ReflectionTypeFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..76d75e0aed5f2082b7f13e9adf274a5d61898231 --- /dev/null +++ b/src/php/Models/Types/Factory/ReflectionTypeFactory.php @@ -0,0 +1,95 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Assert\Assertion; +use Irstea\NgModelGeneratorBundle\Models\PHPClass; +use Irstea\NgModelGeneratorBundle\Models\Types\Objects\InterfaceType; +use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property; +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\PropertyInfoExtractor; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class ReflectionTypeFactory. + */ +final class ReflectionTypeFactory implements TypeFactoryInterface +{ + /** + * @var PropertyInfoExtractor + */ + private $propertyInfoExtractor; + + /** + * ReflectionTypeFactory constructor. + * + * @param PropertyInfoExtractor $propertyInfoExtractor + */ + public function __construct(PropertyInfoExtractor $propertyInfoExtractor) + { + $this->propertyInfoExtractor = $propertyInfoExtractor; + } + + /** + * {@inheritdoc} + */ + public function supportsType(PropertyType $type, ContextInterface $context): bool + { + return $type->getClassName() && \class_exists($type->getClassName()); + } + + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type, ContextInterface $context): Type + { + Assertion::true($this->supportsType($type, $context)); + + $fqcn = $type->getClassName(); + $propertyNames = $this->propertyInfoExtractor->getProperties($fqcn); + + $properties = []; + + foreach ($propertyNames as $propertyName) { + $propertyTypes = $this->propertyInfoExtractor->getTypes($fqcn, $propertyName); + if (!isset($propertyTypes[0])) { + continue; + } + + $propertyType = $propertyTypes[0]; + + $type = $context->createType($propertyType); + + $properties[] = new Property( + $propertyName, + $this->propertyInfoExtractor->getShortDescription($fqcn, $propertyName) ?: '', + $type, + false, + $propertyType->isNullable(), + !$this->propertyInfoExtractor->isWritable($fqcn, $propertyName) + && !$this->propertyInfoExtractor->isInitializable($fqcn, $propertyName) + ); + } + + return new InterfaceType(PHPClass::get($fqcn)->getBaseName(), null, $properties); + } +} diff --git a/src/php/Models/Types/Factory/TypeCache.php b/src/php/Models/Types/Factory/TypeCache.php new file mode 100644 index 0000000000000000000000000000000000000000..6455d4b8cd3a55d7eb8be2c27fb652cab8f692b8 --- /dev/null +++ b/src/php/Models/Types/Factory/TypeCache.php @@ -0,0 +1,57 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; + +/** + * Class TypeCache. + */ +final class TypeCache extends AbstractTypeFactoryDecorator +{ + /** + * @var array + */ + private $cache = []; + + /** + * {@inheritdoc} + */ + public function supportsType(PropertyType $type, ContextInterface $context): bool + { + return \array_key_exists(TypeHelper::getTypeKey($type), $this->cache) || parent::supportsType($type, $context); + } + + /** + * {@inheritdoc} + */ + public function createType(PropertyType $type, ContextInterface $context): Type + { + $key = TypeHelper::getTypeKey($type); + if (\array_key_exists($key, $this->cache)) { + return $this->cache[$key]; + } + + return $this->cache[$key] = parent::createType($type, $context); + } +} diff --git a/src/php/TypeFactoryInterface.php b/src/php/Models/Types/Factory/TypeFactoryInterface.php similarity index 70% rename from src/php/TypeFactoryInterface.php rename to src/php/Models/Types/Factory/TypeFactoryInterface.php index 4b2ae3f9e80eb61b8c3f3260c76ba3afd57dbeb1..085e6c4560e6b280bd65c05be9f108f56208b906 100644 --- a/src/php/TypeFactoryInterface.php +++ b/src/php/Models/Types/Factory/TypeFactoryInterface.php @@ -19,10 +19,10 @@ * <https://www.gnu.org/licenses/>. */ -namespace Irstea\NgModelGeneratorBundle; +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; -use Irstea\NgModelGeneratorBundle\Models\Types\Deferred; use Irstea\NgModelGeneratorBundle\Models\Types\Type; +use Symfony\Component\PropertyInfo\Type as PropertyType; /** * Interface TypeFactoryInterface. @@ -30,29 +30,18 @@ use Irstea\NgModelGeneratorBundle\Models\Types\Type; interface TypeFactoryInterface { /** - * @param string $name + * @param PropertyType $type + * @param ContextInterface $context * * @return bool */ - public function has(string $name): bool; + public function supportsType(PropertyType $type, ContextInterface $context): bool; /** - * @param string $name + * @param PropertyType $type + * @param ContextInterface $context * * @return Type */ - public function get(string $name): Type; - - /** - * @param string $name - * - * @return Deferred - */ - public function defer(string $name): Deferred; - - /** - * @param string $name - * @param Type $type - */ - public function add(string $name, Type $type): void; + public function createType(PropertyType $type, ContextInterface $context): Type; } diff --git a/src/php/Models/Types/Factory/TypeHelper.php b/src/php/Models/Types/Factory/TypeHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..9f7a3ad0744030c053c349eb31d1f06829aff8e8 --- /dev/null +++ b/src/php/Models/Types/Factory/TypeHelper.php @@ -0,0 +1,92 @@ +<?php declare(strict_types=1); +/* + * This file is part of "irstea/ng-model-generator-bundle". + * + * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. + * Copyright (C) 2018-2019 IRSTEA + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License and the GNU + * Lesser General Public License along with this program. If not, see + * <https://www.gnu.org/licenses/>. + */ + +namespace Irstea\NgModelGeneratorBundle\Models\Types\Factory; + +use Assert\Assertion; +use Symfony\Component\PropertyInfo\Type; + +/** + * Class TypeHelper. + */ +final class TypeHelper +{ + /** + * Retourne une chaîne permettant d'identifier un type. + * + * @param Type $type + * + * @return string + */ + public static function getTypeKey(Type $type): string + { + if ($type->isNullable()) { + return '?' . self::getTypeKey(self::notNullable($type)); + } + + if ($type->isCollection()) { + if ($type->getCollectionValueType()) { + return self::getTypeKey($type->getCollectionValueType()) . '[]'; + } + + return 'array'; + } + + if ($type->getClassName()) { + return trim($type->getClassName(), '\\'); + } + + return $type->getBuiltinType(); + } + + /** + * @param string $className + * + * @return Type + */ + public static function fromClassName(string $className): Type + { + Assertion::classExists($className); + + return new Type('object', false, $className); + } + + /** + * @param Type $type + * + * @return Type + */ + public static function notNullable(Type $type): Type + { + if (!$type->isNullable()) { + return $type; + } + + return new Type( + $type->getBuiltinType(), + false, + $type->getClassName(), + $type->isCollection(), + $type->getCollectionKeyType(), + $type->getCollectionValueType() + ); + } +} diff --git a/src/php/OperationMapper.php b/src/php/OperationMapper.php index 20f5c91068239322b2d9f5f4ccc76810b02a6222..30c35ff095b3801ad50027fdf26c1a52f137f2d9 100644 --- a/src/php/OperationMapper.php +++ b/src/php/OperationMapper.php @@ -30,6 +30,7 @@ use Irstea\NgModelGeneratorBundle\Models\PHPClass; use Irstea\NgModelGeneratorBundle\Models\Types\AbstractCollection; use Irstea\NgModelGeneratorBundle\Models\Types\ArrayType; use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType; +use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeFactoryInterface; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\AnonymousObject; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\InterfaceType; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property; @@ -39,7 +40,6 @@ use Irstea\NgModelGeneratorBundle\Models\Types\Operations\Path; use Irstea\NgModelGeneratorBundle\Models\Types\Placeholder; use Irstea\NgModelGeneratorBundle\Models\Types\Reference; use Irstea\NgModelGeneratorBundle\Models\Types\Resources\Collection; -use Irstea\NgModelGeneratorBundle\Models\Types\Resources\IRI; use Irstea\NgModelGeneratorBundle\Models\Types\Type; use Irstea\NgModelGeneratorBundle\Models\Types\Union; @@ -182,7 +182,7 @@ final class OperationMapper !$this->operation->getOpDef()->isCreateItem() ); - return $mapper->get($serializationMetadata->getRoot()->getFullName()); + return $mapper->create($serializationMetadata->getRoot()->getFullName()); } /** @@ -250,7 +250,7 @@ final class OperationMapper $filterType = PHPClass::get(get_class($filter))->getBaseName(); foreach ($filter->getDescription($this->operation->getResource()->getFullName()) as $name => $filterDesc) { - $type = $this->typeFactory->get($filterDesc['type']); + $type = $this->typeFactory->create($filterDesc['type']); if (isset($filterDesc['property']) && is_string($filterDesc['property'])) { $propType = $this->resolvePropertyType($rootClass, $filterDesc['property']); @@ -304,7 +304,7 @@ final class OperationMapper private function resolvePropertyType(string $class, string $property): ?Type { /** @var AnonymousObject|null $type */ - $type = $this->typeFactory->get($class)->findType(AnonymousObject::class); + $type = $this->typeFactory->create($class)->findType(AnonymousObject::class); if (!$type || !$type->hasProperty($property)) { return null; } @@ -329,7 +329,7 @@ final class OperationMapper break; case 'OrderFilter': - return $this->typeFactory->get('Ordering'); + return $this->typeFactory->create('Ordering'); case 'BooleanFilter': case 'ExistFilter': diff --git a/src/php/Resources/config/config.xml b/src/php/Resources/config/config.xml index 20f06f5db757685dc3c43df080208dfafae961f4..ea112ca83f74c454d444215de4427ded1a4223a3 100644 --- a/src/php/Resources/config/config.xml +++ b/src/php/Resources/config/config.xml @@ -6,8 +6,7 @@ <services> - <service id="ng_model_generator.metadata.factory" lazy="true" - class="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory"> + <service id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory" lazy="true"> <argument type="service" id="api_platform.resource_class_resolver"/> <argument type="service" id="api_platform.metadata.resource.metadata_factory"/> <argument type="service" id="api_platform.metadata.property.name_collection_factory"/> @@ -16,65 +15,69 @@ <argument type="service" id="api_platform.operation_method_resolver"/> <argument type="service" id="router"/> <argument type="service" id="api_platform.filter_locator"/> - <argument type="service" id="ng_model_generator.metadata.pagination"/> - <argument type="service" id="irstea_ng_model_generator.metadata.resource_class_hierarchy"/> + <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\PaginationMetadata"/> + <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\ResourceClassHierarchy"/> </service> - <service id="ng_model_generator.metadata.caching_factory" lazy="true" public="false" - decorates="ng_model_generator.metadata.factory" - class="Irstea\NgModelGeneratorBundle\Metadata\CachingMetadataFactory"> - <argument type="service" id="ng_model_generator.metadata.caching_factory.inner"/> + <service id="Irstea\NgModelGeneratorBundle\Metadata\CachingMetadataFactory" lazy="true" public="false" + decorates="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory"> + <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\CachingMetadataFactory.inner"/> <argument type="service" id="doctrine_cache.providers.ng_model_generator_metadata_cache" on-invalid="null"/> </service> - <service id="irstea_ng_model_generator.metadata.resource_class_hierarchy" - class="Irstea\NgModelGeneratorBundle\Metadata\ResourceClassHierarchy"> - <argument id="irstea_ng_model_generator.resource_name_collection" type="service"/> + <service id="Irstea\NgModelGeneratorBundle\Metadata\ResourceClassHierarchy"> + <argument id="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection" type="service"/> </service> - <service id="ng_model_generator.metadata.pagination" lazy="true" - class="Irstea\NgModelGeneratorBundle\Metadata\PaginationMetadata"> + <service id="Irstea\NgModelGeneratorBundle\Metadata\PaginationMetadata" lazy="true"> <argument>%api_platform.collection.pagination.enabled%</argument> <argument>%api_platform.collection.pagination.page_parameter_name%</argument> <argument>%api_platform.collection.pagination.client_items_per_page%</argument> <argument>%api_platform.collection.pagination.items_per_page_parameter_name%</argument> </service> - <service id="ng_model_generator.command.ng_model_generate_command" lazy="true" - class="Irstea\NgModelGeneratorBundle\Command\NgModelGenerateCommand"> - <argument type="service" id="irstea_ng_model_generator.model_generator"/> - <argument type="service" id="irstea_ng_model_generator.documentation"/> + <service id="Irstea\NgModelGeneratorBundle\Command\NgModelGenerateCommand" lazy="true"> + <argument type="service" id="Irstea\NgModelGeneratorBundle\ModelGenerator"/> + <argument type="service" id="ApiPlatform\Core\Documentation\Documentation"/> <tag name="console.command"/> </service> - <service id="ng_model_generator.command.ng_model_metadata_command" lazy="true" - class="Irstea\NgModelGeneratorBundle\Command\NgModelMetadataCommand"> - <argument type="service" id="ng_model_generator.metadata.factory"/> - <argument type="service" id="irstea_ng_model_generator.documentation"/> + <service + id="Irstea\NgModelGeneratorBundle\Command\NgModelMetadataCommand" + lazy="true" + > + <argument type="service" id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory"/> + <argument type="service" id="ApiPlatform\Core\Documentation\Documentation"/> <tag name="console.command"/> </service> - <service id="irstea_ng_model_generator.model_generator" lazy="true" class="Irstea\NgModelGeneratorBundle\ModelGenerator"> - <argument id="ng_model_generator.metadata.factory" type="service"/> + <service id="Irstea\NgModelGeneratorBundle\ModelGenerator" + lazy="true" + > + <argument id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory" type="service"/> + <argument id="Irstea\NgModelGeneratorBundle\SerializationMapperFactory" type="service"/> <argument id="twig" type="service"/> </service> - <service id="irstea_ng_model_generator.documentation" lazy="true" class="ApiPlatform\Core\Documentation\Documentation"> - <argument type="service" id="irstea_ng_model_generator.resource_name_collection"/> + <service id="ApiPlatform\Core\Documentation\Documentation" lazy="true"> + <argument type="service" id="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection"/> <argument>%api_platform.title%</argument> <argument>%api_platform.description%</argument> <argument>%api_platform.version%</argument> <argument>%api_platform.formats%</argument> </service> - <service id="irstea_ng_model_generator.resource_name_collection" lazy="true" public="false" - class="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection"> + <service id="Irstea\NgModelGeneratorBundle\Models\Types\Factory\BuiltinTypeProvider" lazy="true"> + <argument id="Irstea\NgModelGeneratorBundle\Metadata\MetadataFactory" type="service"/> + </service> + + <service id="ApiPlatform\Core\Metadata\Resource\ResourceNameCollection" lazy="true" public="false"> <factory service="api_platform.metadata.resource.name_collection_factory" method="create"/> </service> - <service class="Irstea\NgModelGeneratorBundle\Twig\GeneratorExtension" id="irstea_ng_model_generator.twig.generator_extension"> + <service id="Irstea\NgModelGeneratorBundle\Twig\GeneratorExtension"> <tag name="twig.extension"/> </service> diff --git a/src/php/SerializationMapper.php b/src/php/SerializationMapper.php index a310632cac02d6b9611ce862c0d3e685cb5c4c44..67a92a3c6604d4eca08230b669e1e50a15085e49 100644 --- a/src/php/SerializationMapper.php +++ b/src/php/SerializationMapper.php @@ -30,9 +30,9 @@ use Irstea\NgModelGeneratorBundle\Models\ClassInfo; use Irstea\NgModelGeneratorBundle\Models\ClassName; use Irstea\NgModelGeneratorBundle\Models\PHPClass; use Irstea\NgModelGeneratorBundle\Models\Types\Alias; -use Irstea\NgModelGeneratorBundle\Models\Types\ArrayType; -use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType; -use Irstea\NgModelGeneratorBundle\Models\Types\Deferred; +use Irstea\NgModelGeneratorBundle\Models\Types\Factory\ContextInterface; +use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeFactoryInterface; +use Irstea\NgModelGeneratorBundle\Models\Types\Factory\TypeHelper; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property; use Irstea\NgModelGeneratorBundle\Models\Types\Reference; use Irstea\NgModelGeneratorBundle\Models\Types\Resources\IRI; @@ -40,16 +40,13 @@ use Irstea\NgModelGeneratorBundle\Models\Types\Resources\Representation; use Irstea\NgModelGeneratorBundle\Models\Types\StringConst; use Irstea\NgModelGeneratorBundle\Models\Types\Type; use Irstea\NgModelGeneratorBundle\Models\Types\Union; -use Symfony\Component\PropertyInfo\Type as APIType; +use Symfony\Component\PropertyInfo\Type as PropertyType; /** * Class SerializationMapper. */ final class SerializationMapper implements TypeFactoryInterface { - /** @var TypeFactoryInterface */ - private $typeFactory; - /** @var SerializationMetadata */ private $serialization; @@ -62,16 +59,13 @@ final class SerializationMapper implements TypeFactoryInterface /** * SerializationMapper constructor. * - * @param TypeFactoryInterface $typeFactory * @param SerializationMetadata $serialization * @param bool $withAtFields */ public function __construct( - TypeFactoryInterface $typeFactory, SerializationMetadata $serialization, bool $withAtFields ) { - $this->typeFactory = $typeFactory; $this->serialization = $serialization; $this->withAtFields = $withAtFields; } @@ -79,53 +73,32 @@ final class SerializationMapper implements TypeFactoryInterface /** * {@inheritdoc} */ - public function has(string $name): bool + public function supportsType(PropertyType $type, ContextInterface $context): bool { - return $this->typeFactory->has($name); - } + $className = $type->getClassName(); - /** - * {@inheritdoc} - */ - public function add(string $name, Type $type): void - { - $this->typeFactory->add($name, $type); + return $className && $this->serialization->hasRepresentationOf(PHPClass::get($className)); } /** * {@inheritdoc} */ - public function defer(string $name): Deferred + public function createType(PropertyType $type, ContextInterface $context): Type { - return $this->typeFactory->defer($name); - } + Assertion::true($this->createType($type, $context)); - /** - * {@inheritdoc} - */ - public function get(string $name): Type - { - if (class_exists($name)) { - $class = PHPClass::get($name); - if ($this->serialization->hasRepresentationOf($class)) { - $repr = $this->serialization->getRepresentationOf($class); - - return $this->defer($repr->getName()) - ->resolveWith( - function () use ($repr) { - return $this->mapRepresentation($repr); - } - ); - } - } + $class = PHPClass::get($type->getClassName()); + $repr = $this->serialization->getRepresentationOf($class); - return $this->typeFactory->get($name); + return $this->mapRepresentation($repr, $context); } /** + * @param ContextInterface $context + * * @return array */ - public function getResourceData(): array + public function getResourceData(ContextInterface $context): array { $resource = $this->serialization->getRoot(); $resourceName = $resource->getFullName(); @@ -142,15 +115,16 @@ final class SerializationMapper implements TypeFactoryInterface $identifier = $identifiers ? array_shift($identifiers) : null; - return [$this->get($resourceName), $identifier, $properties]; + return [$context->createType(TypeHelper::fromClassName($resourceName)), $identifier, $properties]; } /** * @param RepresentationMetadata $repr + * @param ContextInterface $context * * @return Type */ - private function mapRepresentation(RepresentationMetadata $repr): Type + private function mapRepresentation(RepresentationMetadata $repr, ContextInterface $context): Type { $classInfo = $this->getClassInfo($repr); @@ -168,7 +142,7 @@ final class SerializationMapper implements TypeFactoryInterface /** @var Type[] $types */ $types = []; foreach ($classInfo->iterateInterfaceDescendants() as $child) { - $types[] = $this->get($child->getFullName()); + $types[] = $context->createType(TypeHelper::fromClassName($child->getFullName())); } switch (\count($types)) { @@ -185,22 +159,22 @@ final class SerializationMapper implements TypeFactoryInterface } if ($classInfo->isIRI()) { - return $this->createIRI([$classInfo]); + return $this->createIRI([$classInfo], $context); } $parent = null; $parentInfo = $classInfo->getParent(); if ($parentInfo !== null && $parentInfo->isInterface()) { - $parent = $this->get($parentInfo->getFullName()); + $parent = $context->createType(TypeHelper::fromClassName($parentInfo->getFullName())); } - $properties = $this->mapProperties($classInfo); + $properties = $this->mapProperties($classInfo, $context); $children = []; /* @var ClassName $class */ foreach ($classInfo->getChildren() as $child) { if ($child->isInterface()) { - $children[] = $this->get($child->getFullName()); + $children[] = $context->createType(TypeHelper::fromClassName($child->getFullName())); } } @@ -208,16 +182,17 @@ final class SerializationMapper implements TypeFactoryInterface } /** - * @param array $resources + * @param array $resources + * @param ContextInterface $context * * @return IRI */ - private function createIRI(array $resources): IRI + private function createIRI(array $resources, ContextInterface $context): IRI { return new IRI( array_map( - function (ClassName $class) { - return $this->typeFactory->get($class->getFullName()); + function (ClassName $class) use ($context) { + return $context->createType(TypeHelper::fromClassName($class->getFullName())); }, $resources ) @@ -225,11 +200,12 @@ final class SerializationMapper implements TypeFactoryInterface } /** - * @param ClassInfo $classInfo + * @param ClassInfo $classInfo + * @param ContextInterface $context * * @return array */ - private function mapProperties(ClassInfo $classInfo): array + private function mapProperties(ClassInfo $classInfo, ContextInterface $context): array { $properties = []; $identifierCount = 0; @@ -240,7 +216,7 @@ final class SerializationMapper implements TypeFactoryInterface ++$identifierCount; } - $property = $this->mapProperty($propertyMeta); + $property = $this->mapProperty($propertyMeta, $context); $properties[$property->getName()] = $property; } @@ -258,7 +234,7 @@ final class SerializationMapper implements TypeFactoryInterface $properties['@id'] = new Property( '@id', '', - $this->createIRI(\iterator_to_array($classInfo->iterateConcreteDescendants())), + $this->createIRI(\iterator_to_array($classInfo->iterateConcreteDescendants()), $context), true, !$this->serialization->isNormalization(), true @@ -294,62 +270,22 @@ final class SerializationMapper implements TypeFactoryInterface /** * @param PropertyMetadata $propertyMeta + * @param ContextInterface $context * * @return Property */ - private function mapProperty(PropertyMetadata $propertyMeta): Property + private function mapProperty(PropertyMetadata $propertyMeta, ContextInterface $context): Property { return new Property( $propertyMeta->getName(), $propertyMeta->getDescription() ?: '', - $this->mapType($propertyMeta->getType(), $propertyMeta->isLink() && !$propertyMeta->isEmbedded()), + $context->createType($propertyMeta->getType()), $propertyMeta->isIdentifier(), $propertyMeta->isNullable(), !$propertyMeta->isWritable() ); } - /** - * @param APIType $type - * @param bool $isLink - * - * @return Type - */ - private function mapType(?APIType $type, bool $isLink = false): Type - { - if ($type === null) { - return BuiltinType::get('unknown'); - } - - if (!$type->isCollection()) { - $className = $type->getClassName(); - if ($className === null) { - return $this->get($type->getBuiltinType()); - } - if ($isLink) { - return $this->createIRI([PHPClass::get($className)]); - } - - return $this->get($className); - } - - $indexType = $type->getCollectionKeyType(); - if ($indexType === null && $type->getCollectionValueType() === null && $type->getBuiltinType() === 'array') { - return new ArrayType(BuiltinType::get('unknown')); - } - - Assertion::notNull($indexType, 'Cannot handle collection with undefined index type'); - - $builtinType = $indexType->getBuiltinType(); - if ($builtinType !== 'int') { - throw new \InvalidArgumentException("Cannot handle collection with index type $builtinType"); - } - - $valueType = $this->mapType($type->getCollectionValueType(), $isLink); - - return new ArrayType($valueType); - } - /** * @param ClassName $class * diff --git a/src/php/SerializationMapperFactory.php b/src/php/SerializationMapperFactory.php index 58e0be36cd7de7fffd99052104528761a4ac19c2..1e78889e84c844630c5d8401ad3a87ac866899a2 100644 --- a/src/php/SerializationMapperFactory.php +++ b/src/php/SerializationMapperFactory.php @@ -28,22 +28,9 @@ use Irstea\NgModelGeneratorBundle\Metadata\SerializationMetadata; */ final class SerializationMapperFactory implements SerializationMapperFactoryInterface { - /** @var TypeFactoryInterface */ - private $typeFactory; - /** @var SerializationMapper[] */ private $cache = []; - /** - * SerializationMapperFactory constructor. - * - * @param TypeFactoryInterface $typeFactory - */ - public function __construct(TypeFactoryInterface $typeFactory) - { - $this->typeFactory = $typeFactory; - } - /** * @param SerializationMetadata $serialization * @param bool $withAtFields @@ -61,7 +48,7 @@ final class SerializationMapperFactory implements SerializationMapperFactoryInte ); if (!isset($this->cache[$key])) { - $this->cache[$key] = new SerializationMapper($this->typeFactory, $serialization, $withAtFields); + $this->cache[$key] = new SerializationMapper($serialization, $withAtFields); } return $this->cache[$key]; diff --git a/src/php/TypeFactory.php b/src/php/TypeFactory.php deleted file mode 100644 index a9422d89402bb72001c72c7c0e17b1369560015d..0000000000000000000000000000000000000000 --- a/src/php/TypeFactory.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php declare(strict_types=1); -/* - * This file is part of "irstea/ng-model-generator-bundle". - * - * "irstea/ng-model-generator-bundle" generates Typescript interfaces for Angular using api-platform metadata. - * Copyright (C) 2018-2019 IRSTEA - * - * This program is free software: you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License as published by the Free - * Software Foundation, either version 3 of the License, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License and the GNU - * Lesser General Public License along with this program. If not, see - * <https://www.gnu.org/licenses/>. - */ - -namespace Irstea\NgModelGeneratorBundle; - -use Irstea\NgModelGeneratorBundle\Exceptions\TypeAlreadyExistsException; -use Irstea\NgModelGeneratorBundle\Exceptions\TypeNotFoundException; -use Irstea\NgModelGeneratorBundle\Models\Types\Deferred; -use Irstea\NgModelGeneratorBundle\Models\Types\Reference; -use Irstea\NgModelGeneratorBundle\Models\Types\Type; - -/** - * Class TypeFactory. - */ -final class TypeFactory implements TypeFactoryInterface, \IteratorAggregate -{ - /** @var Type[] */ - private $types = []; - - /** - * {@inheritdoc} - */ - public function getIterator() - { - yield from $this->types; - } - - /** - * @param string $name - * - * @return bool - */ - public function has(string $name): bool - { - return isset($this->types[$name]); - } - - /** - * @param string $name - * - * @return Type - */ - public function get(string $name): Type - { - // API-platform semble avoir un problème avec les types utilisés dans un Trait. - if (strpos($name, '\UuidInterface') !== false) { - $name = 'Ramsey\Uuid\UuidInterface'; - } - - if (!isset($this->types[$name])) { - throw new TypeNotFoundException("unknown type ${name}"); - } - - return $this->types[$name]; - } - - /** - * {@inheritdoc} - */ - public function add(string $name, Type $type): void - { - if (isset($this->types[$name])) { - throw new TypeAlreadyExistsException("type already exists: ${name}"); - } - - $this->types[$name] = $type; - } - - /** - * {@inheritdoc} - */ - public function defer(string $name): Deferred - { - if (isset($this->types[$name]) && $this->types[$name] instanceof Deferred) { - /* @noinspection PhpIncompatibleReturnTypeInspection */ - return $this->types[$name]; - } - $ref = new Reference($name); - $this->add($name, $ref); - - return $ref; - } -}