Commit 59a0bce5 authored by Guillaume Perréal's avatar Guillaume Perréal
Browse files

Refactoring pour simplifier l'optimisation des représentations vides.

parent cac0db1e
<?php declare(strict_types=1);
/**
/*
* irstea/ng-model-generator-bundle generates Typescript interfaces for Angular using api-platform metadata.
* Copyright (C) 2018 IRSTEA
* All rights reserved.
* @copyright 2018 IRSTEA
* @author guillaume.perreal
*
* 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\Iterators;
/**
* Class UniqueFilter
* Class UniqueFilter.
*/
final class UniqueFilter
{
......@@ -25,18 +36,15 @@ final class UniqueFilter
*/
public function __invoke($value): bool
{
$key = is_object($value) ? spl_object_hash($value) : (string)$value;
if (isset($this->seen[$key])) {
return false;
}
$key = is_object($value) ? spl_object_hash($value) : (string) $value;
$alreadySeen = isset($this->seen[$key]);
$this->seen[$key] = true;
return true;
return !$alreadySeen;
}
/**
*
*/
public function reset(): void {
public function reset(): void
{
$this->seen = [];
}
}
<?php declare(strict_types=1);
/*
* irstea/ng-model-generator-bundle generates Typescript interfaces for Angular using api-platform metadata.
* Copyright (C) 2018 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;
/**
* Interface Declaration.
*/
interface Declaration
{
/**
* @return string
*/
public function getName(): string;
/**
* @return string
*/
public function getDeclaration(): string;
}
......@@ -19,6 +19,8 @@
namespace Irstea\NgModelGeneratorBundle\Models\Types;
use Irstea\NgModelGeneratorBundle\FQCN;
/**
* Class AbstractType.
*/
......@@ -33,10 +35,10 @@ abstract class AbstractType implements Type
}
/**
* {@inheritdoc}
* @return string
*/
public function getDeclaration(string $name): string
public function __toString()
{
return sprintf('export type %s = %s', $name, $this->getUsage());
return sprintf('%s(#%x)', FQCN::baseName(get_class($this)), spl_object_hash($this));
}
}
......@@ -17,22 +17,39 @@
* <https://www.gnu.org/licenses/>.
*/
namespace Irstea\NgModelGeneratorBundle\Models\Operations;
namespace Irstea\NgModelGeneratorBundle\Models\Types;
use Irstea\NgModelGeneratorBundle\Models\Types\AnonymousObject;
use Irstea\NgModelGeneratorBundle\Models\Types\Type;
use Irstea\NgModelGeneratorBundle\Models\Declaration;
use Irstea\NgModelGeneratorBundle\Models\NamedTrait;
/**
* Class TransformedParameter.
* Class Alias.
*/
final class TransformedParameter extends Parameter
final class Alias extends AbstractType implements Declaration
{
use NamedTrait;
/** @var Type */
private $target;
/**
* Alias constructor.
*
* @param string $name
* @param Type $target
*/
public function __construct(string $name, Type $target)
{
$this->name = $name;
$this->target = $target;
}
/**
* {@inheritdoc}
*/
public function __construct(string $name, Type $type, bool $optional = false)
public function getDeclaration(): string
{
parent::__construct($name, $type, $optional);
return sprintf('export type %s = %s;', $this->name, $this->target->getUsage());
}
/**
......@@ -40,24 +57,14 @@ final class TransformedParameter extends Parameter
*/
public function getUsage(): string
{
$components = [];
/** @var AnonymousObject $type */
$type = $this->getType();
$paramName = $this->getName();
foreach ($type->getProperties() as $property) {
$propertyName = $property->getEscapedName();
$rhs = $propertyName !== $property->getName() ? "${paramName}[${propertyName}]" : "${paramName}.${propertyName}";
if ($property->getType()->getUsage() !== 'string') {
$rhs = "''+${rhs}";
}
$components[] = "${propertyName}: ${rhs}";
}
return $this->name;
}
return sprintf('%s: {%s}', $this->getName(), implode(', ', $components));
/**
* {@inheritdoc}
*/
public function getIterator()
{
yield $this->target;
}
}
......@@ -25,18 +25,20 @@ namespace Irstea\NgModelGeneratorBundle\Models\Types;
class AnonymousObject extends AbstractType implements Object
{
/**
* @var array<string,Property>
* @var Property[]
*/
private $properties = [];
/**
* Resource constructor.
*
* @param array<string,Property> $properties
* @param Property[] $properties
*/
public function __construct(array $properties = [])
{
$this->properties = array_values($properties);
foreach ($properties as $property) {
$this->properties[$property->getName()] = $property;
}
}
/**
......@@ -58,15 +60,15 @@ class AnonymousObject extends AbstractType implements Object
/**
* {@inheritdoc}
*/
final public function getDeclaration(string $name): string
final public function getDeclaration(): string
{
return trim($this->getDeclarationHeader($name) . ' ' . $this->getDeclarationBody(true));
return trim($this->getDeclarationHeader() . ' ' . $this->getDeclarationBody(true));
}
/**
* @return string
*/
protected function getDeclarationHeader(string $name): string
protected function getDeclarationHeader(): string
{
return '';
}
......
......@@ -19,11 +19,16 @@
namespace Irstea\NgModelGeneratorBundle\Models\Types;
use Irstea\NgModelGeneratorBundle\Models\Declaration;
use Irstea\NgModelGeneratorBundle\Models\NamedTrait;
/**
* Class ObjectClass.
*/
class ObjectClass extends AnonymousObject
class ObjectClass extends AnonymousObject implements Declaration
{
use NamedTrait;
public const KIND_CLASS = 'class';
public const KIND_INTERFACE = 'interface';
......@@ -37,22 +42,32 @@ class ObjectClass extends AnonymousObject
* ObjectClass constructor.
*
* @param string $kind
* @param string $name
* @param array $parents
* @param array $properties
*/
public function __construct(string $kind, array $parents = [], array $properties = [])
public function __construct(string $kind, string $name, array $parents = [], array $properties = [])
{
parent::__construct($properties);
$this->name = $name;
$this->kind = $kind;
$this->parents = $parents;
}
/**
* @return string
*/
public function getUsage(): string
{
return $this->name;
}
/**
* {@inheritdoc}
*/
protected function getDeclarationHeader(string $name): string
protected function getDeclarationHeader(): string
{
return implode(' ', ['export', $this->kind, $name, $this->getInheritanceDeclaration()]);
return implode(' ', ['export', $this->kind, $this->name, $this->getInheritanceDeclaration()]);
}
/**
......
......@@ -22,7 +22,7 @@ namespace Irstea\NgModelGeneratorBundle\Models\Types;
/**
* Class Ref.
*/
final class Reference extends AbstractType
class Reference extends AbstractType
{
/** @var Type */
private $target;
......@@ -80,4 +80,12 @@ final class Reference extends AbstractType
{
yield $this->target;
}
/**
* {@inheritdoc}
*/
public function __toString()
{
return '&' . $this->target;
}
}
......@@ -19,6 +19,7 @@
namespace Irstea\NgModelGeneratorBundle\Models\Types;
use Irstea\NgModelGeneratorBundle\FQCN;
use Irstea\NgModelGeneratorBundle\Models\Operations\Operation;
/**
......@@ -44,7 +45,7 @@ final class Repository extends ObjectClass
*/
public function __construct(string $resourceName, array $operations)
{
parent::__construct(self::KIND_CLASS, [], []);
parent::__construct(self::KIND_CLASS, FQCN::baseName($resourceName) . 'Repository', [], []);
$this->resourceName = $resourceName;
$this->operations = $operations;
}
......@@ -75,9 +76,9 @@ final class Repository extends ObjectClass
/**
* {@inheritdoc}
*/
protected function getDeclarationHeader(string $name): string
protected function getDeclarationHeader(): string
{
return "@Inject()\n" . parent::getDeclarationHeader($name);
return "@Inject()\n" . parent::getDeclarationHeader();
}
/**
......
......@@ -31,12 +31,13 @@ final class Representation extends ObjectClass
* Representation constructor.
*
* @param string $resourceClass
* @param string $name
* @param array $parents
* @param array $properties
*/
public function __construct(string $resourceClass, array $parents = [], array $properties = [])
public function __construct(string $resourceClass, string $name, array $parents = [], array $properties = [])
{
parent::__construct(self::KIND_INTERFACE, $parents, $properties);
parent::__construct(self::KIND_INTERFACE, $name, $parents, $properties);
$this->resourceClass = $resourceClass;
}
......
......@@ -29,13 +29,6 @@ interface Type extends \IteratorAggregate
*/
public function getUsage(): string;
/**
* @param string $name
*
* @return string
*/
public function getDeclaration(string $name): string;
/**
* @return Type
*/
......
......@@ -124,8 +124,8 @@ final class OperationMapper
$qpName = $this->operation->getResource()->getShortName() . ucfirst($name) . 'Params';
$queryParameters = $this->typeFactory->getOrCreate(
$qpName,
function () use ($queryParameters): Type {
return new ObjectClass(ObjectClass::KIND_INTERFACE, [], $queryParameters);
function () use ($queryParameters, $qpName): Type {
return new ObjectClass(ObjectClass::KIND_INTERFACE, $qpName, [], $queryParameters);
}
);
}
......
......@@ -98,10 +98,12 @@ final class ResourceTypeFactory implements TypeFactoryInterface
}
if ($this->metadataFactory->isResource($name)) {
$reprName = $this->getRepresentationName($name);
return $this->typeFactory->getOrCreate(
$this->getRepresentationName($name),
function () use ($name): Type {
return $this->mapRepresentation($name);
$reprName,
function () use ($name, $reprName): Type {
return $this->mapRepresentation($name, $reprName);
}
);
}
......@@ -143,7 +145,7 @@ final class ResourceTypeFactory implements TypeFactoryInterface
*
* @return Type
*/
private function mapRepresentation(string $resourceClass): Type
private function mapRepresentation(string $resourceClass, string $name): Type
{
$resourceMeta = $this->metadataFactory->getResourceMetadata($resourceClass);
......@@ -161,7 +163,7 @@ final class ResourceTypeFactory implements TypeFactoryInterface
);
}
return new Representation($resourceClass, [], $properties);
return new Representation($resourceClass, $name, [], $properties);
}
/**
......
......@@ -20,7 +20,12 @@
namespace Irstea\NgModelGeneratorBundle;
use ApiPlatform\Core\Documentation\Documentation;
use Irstea\NgModelGeneratorBundle\Iterators\RecursorIterator;
use Irstea\NgModelGeneratorBundle\Iterators\UniqueFilter;
use Irstea\NgModelGeneratorBundle\Models\Declaration;
use Irstea\NgModelGeneratorBundle\Models\Metadata\MetadataFactoryInterface;
use Irstea\NgModelGeneratorBundle\Models\Types\Alias;
use Irstea\NgModelGeneratorBundle\Models\Types\Reference;
use Irstea\NgModelGeneratorBundle\Models\Types\Repository;
use Irstea\NgModelGeneratorBundle\Models\Types\Representation;
use Irstea\NgModelGeneratorBundle\Models\Types\Type;
......@@ -70,13 +75,32 @@ final class Serializer implements NormalizerInterface, EncoderInterface
}
$typeFactory = $this->createTypeFactory();
$this->extractRepositories($doc, $typeFactory);
$repos = $this->extractRepositories($doc, $typeFactory);
/** @var array<string,TypeDeclaration> $declarations */
$resources = [];
$iriType = $typeFactory->get('IRI');
foreach ($typeFactory as $ref) {
if (!($ref instanceof Reference)) {
continue;
}
$repr = $ref->dereference();
if (!($repr instanceof Representation)) {
continue;
}
if (!$repr->hasNonIdentifierProperty()) {
$ref->setTarget($iriType);
}
}
$declarations = [];
$repoClasses = [];
$resources = [];
foreach ($typeFactory->all() as $name => $type) {
foreach ($this->recursivelyIterateOnce($repos) as $type) {
if (!($type instanceof Declaration)) {
continue;
}
$name = $type->getName();
$declarations[$name] = $type;
if ($type instanceof Representation) {
$resources[] = $name;
}
......@@ -85,22 +109,20 @@ final class Serializer implements NormalizerInterface, EncoderInterface
}
}
$data = [
return [
'title' => $doc->getTitle(),
'version' => $doc->getVersion(),
'description' => $doc->getDescription() ?: '',
'declarations' => $typeFactory->all(),
'declarations' => $declarations,
'repositories' => $repoClasses,
'resources' => $resources,
];
return $data;
}
/**
* @return TypeFactoryInterface
*/
private function createTypeFactory(): TypeFactoryInterface
private function createTypeFactory(): TypeFactory
{
$factory = new TypeFactory();
......@@ -108,9 +130,14 @@ final class Serializer implements NormalizerInterface, EncoderInterface
$factory->addBuiltin($builtin);
}
$factory->getOrCreate('UUID', [$factory, 'get'], 'string');
$factory->getOrCreate('IRI', [$factory, 'get'], 'string');
$factory->getOrCreate('DateTime', [$factory, 'get'], 'string');
foreach (['UUID', 'IRI', 'DateTime'] as $name) {
$factory->getOrCreate(
$name,
function () use ($name, $factory) {
return new Alias($name, $factory->get('string'));
}
);
}
foreach ([
PHPType::BUILTIN_TYPE_ARRAY => 'Array',
......@@ -143,8 +170,10 @@ final class Serializer implements NormalizerInterface, EncoderInterface
foreach ($doc->getResourceNameCollection() as $className) {
$resourceMeta = $this->metadataFactory->getResourceMetadata($className);
$repos[] = $typeFactory->getOrCreate(
$resourceMeta->getShortName() . 'Repository',
$repoName = $resourceMeta->getShortName() . 'Repository';
$repos[$repoName] = $typeFactory->getOrCreate(
$repoName,
function () use ($resourceMeta, $typeFactory): Type {
$operations = [];
......@@ -158,9 +187,37 @@ final class Serializer implements NormalizerInterface, EncoderInterface
);
}
ksort($repos);
return $repos;
}
/**
* @param \Iterator|\IteratorAggregate|array $traversable
*
* @return \Iterator
*/
private function recursivelyIterateOnce($traversable): \Iterator
{
if (\is_array($traversable)) {
$inner = new \ArrayIterator($traversable);
} elseif ($traversable instanceof \IteratorAggregate) {
$inner = $traversable->getIterator();
} elseif ($traversable instanceof \Iterator) {
$inner = $traversable;
} else {
throw new \InvalidArgumentException('expected a traversable value');
}
return new \RecursiveIteratorIterator(
new \RecursiveCallbackFilterIterator(
new RecursorIterator($inner),
new UniqueFilter()
),
\RecursiveIteratorIterator::CHILD_FIRST
);
}
/**
* {@inheritdoc}
*/
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment