diff --git a/src/Models/Types/Operations/PlainPath.php b/src/Models/Types/Operations/FixedPathPart.php similarity index 61% rename from src/Models/Types/Operations/PlainPath.php rename to src/Models/Types/Operations/FixedPathPart.php index ff4c6b00bf91dba82bb529ff9f60173222cf0e51..1146dfeb4ed13bf9357fdfb629b2f9f5e6ad0ef1 100644 --- a/src/Models/Types/Operations/PlainPath.php +++ b/src/Models/Types/Operations/FixedPathPart.php @@ -19,63 +19,69 @@ namespace Irstea\NgModelGeneratorBundle\Models\Types\Operations; -use Irstea\NgModelGeneratorBundle\TypescriptHelper; - /** - * Class PlainPath. + * Class FixedPathPart. */ -class PlainPath implements Path, \JsonSerializable +final class FixedPathPart implements PathPart { - /** @var string */ - private $path; + /** + * @var string + */ + private $part; /** - * PlainPath constructor. - * - * @param string $path + * FixedPathPart constructor. */ - public function __construct(string $path) + public function __construct(string $part) { - $this->path = $path; + $this->part = $part; } /** * {@inheritdoc} */ - public function getUsage(): string + public function asTemplate(): string { - return TypescriptHelper::quoteString($this->path); + return $this->part; } /** * {@inheritdoc} */ - public function getParameters(): array + public function asTestPattern(): string { - return []; + return \preg_quote($this->part, '/'); } /** * {@inheritdoc} */ - public function getPattern(): string + public function asCapturePattern(): string { - return preg_quote($this->path, '/'); + return $this->asTestPattern(); } /** - * @return Parameter|null + * {@inheritdoc} */ - public function getIdentifier(): ?Parameter + public function getParameter(): ?Parameter { return null; } /** - * @return mixed|string + * {@inheritdoc} + */ + public function isEqual(PathPart $other): bool + { + return $other instanceof self && $other->part === $this->part; + } + + /** + * @return array */ public function jsonSerialize() { - return $this->path; + return \get_object_vars($this); } } diff --git a/src/Models/Types/Operations/Parameter.php b/src/Models/Types/Operations/Parameter.php index 146e397f185129ffc4f8144fc6d5b43407df23e2..bd40905b1362d516b6bc95f6c5fa9ddb1b0e24e5 100644 --- a/src/Models/Types/Operations/Parameter.php +++ b/src/Models/Types/Operations/Parameter.php @@ -89,15 +89,29 @@ class Parameter implements \IteratorAggregate, \JsonSerializable yield $this->type; } + /** + * @param self $other + * + * @return bool + */ + public function isEqual(self $other): bool + { + /* @noinspection TypeUnsafeComparisonInspection */ + return $other instanceof self && + $other->name === $this->name && + $other->optional === $this->optional && + $other->type == $this->type; + } + /** * @return array|mixed */ public function jsonSerialize() { return [ - 'name' => $this->name, - 'type' => $this->type, - 'required' => !$this->optional, + 'name' => $this->name, + 'type' => $this->type, + 'required' => !$this->optional, ]; } } diff --git a/src/Models/Types/Operations/ParameterPathPart.php b/src/Models/Types/Operations/ParameterPathPart.php new file mode 100644 index 0000000000000000000000000000000000000000..bcb47c97e3e389b59bb5b4bb5bd6bf741cb2b460 --- /dev/null +++ b/src/Models/Types/Operations/ParameterPathPart.php @@ -0,0 +1,104 @@ +<?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\Types\Operations; + +/** + * Class ParameterPathPart. + */ +final class ParameterPathPart implements PathPart +{ + /** + * @var Parameter + */ + private $parameter; + + /** + * @var string + */ + private $requirement; + + /** + * ParameterPathPart constructor. + * + * @param Parameter $parameter + * @param string $requirement + */ + public function __construct(Parameter $parameter, string $requirement = '[^/]+') + { + $this->parameter = $parameter; + $this->requirement = $requirement; + } + + /** + * {@inheritdoc} + */ + public function asTemplate(): string + { + return sprintf('${%s}', $this->parameter->getName()); + } + + /** + * {@inheritdoc} + */ + public function asTestPattern(): string + { + return $this->requirement; + } + + /** + * {@inheritdoc} + */ + public function asCapturePattern(): string + { + return sprintf('(%s)', $this->requirement); + } + + /** + * {@inheritdoc} + */ + public function getParameter(): ?Parameter + { + return $this->parameter; + } + + /** + * {@inheritdoc} + */ + public function isEqual(PathPart $other): bool + { + return $other instanceof self && $other->parameter->isEqual($this->parameter); + } + + /** + * @return \Generator + */ + public function getIterator() + { + yield $this->parameter; + } + + /** + * @return array + */ + public function jsonSerialize() + { + return \get_object_vars($this); + } +} diff --git a/src/Models/Types/Operations/Path.php b/src/Models/Types/Operations/Path.php index 61ef73a85157af5d8d06d6b15a1245c7511a29bc..b14e9d9a96a707c78eccea7b2475a2ac83f684f0 100644 --- a/src/Models/Types/Operations/Path.php +++ b/src/Models/Types/Operations/Path.php @@ -19,28 +19,157 @@ namespace Irstea\NgModelGeneratorBundle\Models\Types\Operations; +use Irstea\NgModelGeneratorBundle\TypescriptHelper; + /** - * Interface Path. + * Class Path. */ -interface Path +final class Path { + /** + * @var PathPart[] + */ + private $parts; + + /** + * Path constructor. + * + * @param PathPart[] $parts + */ + public function __construct(array $parts) + { + $this->parts = $parts; + } + + /** + * @return string + */ + public function getUsage(): string + { + return TypescriptHelper::quoteString( + $this->getTemplate(), + $this->hasParameters() ? '`' : "'" + ); + } + + /** + * @return string + */ + public function getTemplate(): string + { + return implode( + '', + array_map( + function (PathPart $part): string { + return $part->asTemplate(); + }, + $this->parts + ) + ); + } + /** * @return string */ - public function getUsage(): string; + public function getTestPattern(): string + { + return implode( + '', + array_map( + function (PathPart $part): string { + return $part->asTestPattern(); + }, + $this->parts + ) + ); + } /** * @return string */ - public function getPattern(): string; + public function getCapturePattern(): string + { + return implode( + '', + array_map( + function (PathPart $part): string { + return $part->asCapturePattern(); + }, + $this->parts + ) + ); + } + + /** + * @return bool + */ + public function hasParameters(): bool + { + foreach ($this->parts as $part) { + if ($part->getParameter() !== null) { + return true; + } + } + + return false; + } /** * @return Parameter[] */ - public function getParameters(): array; + public function getParameters(): array + { + return \array_filter( + array_map( + function (PathPart $part): ?Parameter { + return $part->getParameter(); + }, + $this->parts + ) + ); + } + + /** + * @param Path $other + * @param Parameter $otherParameter + * + * @return Path + */ + public function buildUsing(Path $other, Parameter $otherParameter): Path + { + if (\count($other->parts) > \count($this->parts)) { + return $this; + } + foreach ($other->parts as $index => $part) { + if (!$this->parts[$index]->isEqual($part)) { + return $this; + } + } + if (\count($other->parts) === \count($this->parts)) { + return $other; + } + + return new Path( + \array_merge( + [new ParameterPathPart($otherParameter, $other->getTestPattern())], + \array_slice($this->parts, \count($other->parts)) + ) + ); + } + + /** + * @return \Generator + */ + public function getIterator() + { + yield from $this->parts; + } /** - * @return Parameter|null + * @return array */ - public function getIdentifier(): ?Parameter; + public function jsonSerialize() + { + return \get_object_vars($this); + } } diff --git a/src/Models/Types/Operations/PathPart.php b/src/Models/Types/Operations/PathPart.php new file mode 100644 index 0000000000000000000000000000000000000000..0c745d4d55de00804e21e7f088ca66ccf519eb2b --- /dev/null +++ b/src/Models/Types/Operations/PathPart.php @@ -0,0 +1,53 @@ +<?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\Types\Operations; + +/** + * interface PathPart. + */ +interface PathPart +{ + /** + * @return string + */ + public function asTemplate(): string; + + /** + * @return string + */ + public function asTestPattern(): string; + + /** + * @return string + */ + public function asCapturePattern(): string; + + /** + * @return Parameter|null + */ + public function getParameter(): ?Parameter; + + /** + * @param PathPart $other + * + * @return bool + */ + public function isEqual(PathPart $other): bool; +} diff --git a/src/Models/Types/Operations/PathTemplate.php b/src/Models/Types/Operations/PathTemplate.php deleted file mode 100644 index 192f07e60419ca10e2d1410cbd4822c65b08c014..0000000000000000000000000000000000000000 --- a/src/Models/Types/Operations/PathTemplate.php +++ /dev/null @@ -1,108 +0,0 @@ -<?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\Types\Operations; - -use Irstea\NgModelGeneratorBundle\Exceptions\DomainException; -use Irstea\NgModelGeneratorBundle\TypescriptHelper; - -/** - * Class PathTemplate. - */ -class PathTemplate implements Path, \JsonSerializable -{ - /** @var string */ - private $template; - - /** @var string */ - private $pattern; - - /** @var Parameter[] */ - private $parameters = []; - - /** @var string|null */ - private $identifierName; - - /** - * PathTemplate constructor. - * - * @param string $template - * @param string $pattern - * @param Parameter[] $parameters - * @param string|null $identifierName - */ - public function __construct(string $template, string $pattern, array $parameters, ?string $identifierName) - { - $this->template = $template; - $this->pattern = $pattern; - $this->identifierName = $identifierName; - - foreach ($parameters as $parameter) { - $this->parameters[$parameter->getName()] = $parameter; - } - - if ($identifierName && !isset($this->parameters[$identifierName])) { - throw new DomainException("L'identifiant $identifierName n'est pas dans la liste des paramètres"); - } - } - - /** - * Get template. - * - * @return string - */ - public function getUsage(): string - { - return TypescriptHelper::quoteString($this->template, '`'); - } - - /** - * {@inheritdoc} - */ - public function getPattern(): string - { - return $this->pattern; - } - - /** - * Get parameters. - * - * @return Parameter[] - */ - public function getParameters(): array - { - return $this->parameters; - } - - /** - * @return Parameter|null - */ - public function getIdentifier(): ?Parameter - { - return $this->identifierName ? $this->parameters[$this->identifierName] : null; - } - - /** - * @return mixed|array - */ - public function jsonSerialize() - { - return \get_object_vars($this); - } -} diff --git a/src/PathParser.php b/src/PathParser.php index 184c4d44aea5d46463ddce567c2b877d7ea9aaf0..1488d5f9cabe9f495fa0a488945d63b66d68a382 100644 --- a/src/PathParser.php +++ b/src/PathParser.php @@ -19,15 +19,12 @@ namespace Irstea\NgModelGeneratorBundle; -use Irstea\NgModelGeneratorBundle\Models\ClassName; use Irstea\NgModelGeneratorBundle\Models\Types\BuiltinType; use Irstea\NgModelGeneratorBundle\Models\Types\Objects\Property; +use Irstea\NgModelGeneratorBundle\Models\Types\Operations\FixedPathPart; use Irstea\NgModelGeneratorBundle\Models\Types\Operations\Parameter; +use Irstea\NgModelGeneratorBundle\Models\Types\Operations\ParameterPathPart; use Irstea\NgModelGeneratorBundle\Models\Types\Operations\Path; -use Irstea\NgModelGeneratorBundle\Models\Types\Operations\PathTemplate; -use Irstea\NgModelGeneratorBundle\Models\Types\Operations\PlainPath; -use Irstea\NgModelGeneratorBundle\Models\Types\Resources\IRI; -use Irstea\NgModelGeneratorBundle\Models\Types\Union; /** * Class PathParser. @@ -37,19 +34,14 @@ final class PathParser implements PathParserInterface /** @var Property[] */ private $properties; - /** @var ClassName */ - private $resource; - /** * PathParser constructor. * - * @param ClassName $resource * @param Property[] $properties */ - public function __construct(ClassName $resource, array $properties) + public function __construct(array $properties) { $this->properties = $properties; - $this->resource = $resource; } /** @@ -57,65 +49,36 @@ final class PathParser implements PathParserInterface */ public function parse(string $path, array $requirements): Path { - $parts = preg_split('/\{(\w+)\}/', $path, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); - - if (\count($parts) === 1) { - return new PlainPath($parts[0]); - } + $stringParts = preg_split('/\{(\w+)\}/', $path, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); - return $this->parsePathTemplate($parts, $requirements); - } + $parts = []; - /** - * @param string[] $parts - * - * @return PathTemplate - */ - private function parsePathTemplate(array $parts, array $requirements): PathTemplate - { - $template = $pattern = $parameters = []; - $identifier = null; - - $num = \count($parts); - for ($i = 0; $i < $num; $i += 2) { - $plain = $parts[$i]; - $template[] = $plain; - $pattern[] = preg_quote($plain, '/'); + $num = \count($stringParts); + for ($i = 0; $i < $num; ++$i) { + if ($stringParts[$i]) { + $parts[] = new FixedPathPart($stringParts[$i]); + } + ++$i; - if (!isset($parts[$i + 1])) { + if ($i >= $num) { break; } - $name = $parts[$i + 1]; + + $name = $stringParts[$i]; if (isset($this->properties[$name])) { $property = $this->properties[$name]; - $isIdentifier = $property->isIdentifier(); $type = $property->getType(); } else { - $isIdentifier = $name === 'id'; $type = BuiltinType::get('string'); } - if ($isIdentifier) { - $identifier = $name; - //$type = Union::create([$type, new IRI([$this->resource])]); - } - - $template[] = '${'; - $template[] = $name; - $template[] = '}'; - $pattern[] = $isIdentifier ? '(' : '(?:'; - $pattern[] = isset($requirements[$name]) ? trim($requirements[$name], '^$') : '[^\\/]*'; - $pattern[] = ')'; + $parameter = new Parameter($name, $type); + $requirement = isset($requirements[$name]) ? trim($requirements[$name], '^$') : '[^\\/]*'; - $parameters[$name] = new Parameter($name, $type); + $parts[] = new ParameterPathPart($parameter, $requirement); } - return new PathTemplate( - implode('', $template), - implode('', $pattern), - $parameters, - $identifier - ); + return new Path($parts); } }