From 0168fd0cfcdc78ac2e45d1ee6b8f979e69c61c29 Mon Sep 17 00:00:00 2001
From: Perreal Guillaume <guillaume.perreal@irstea.fr>
Date: Tue, 7 May 2019 11:38:13 +0200
Subject: [PATCH] =?UTF-8?q?Utilise=20le=20router=20Symfony=20pour=20d?=
 =?UTF-8?q?=C3=A9terminer=20les=20chemins=20exacts=20de=20ressources.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 composer.json                        |  1 +
 src/php/Metadata/MetadataFactory.php | 70 +++++++++++++++++++++++-----
 src/php/Models/ClassInfo.php         |  6 +--
 src/php/Resources/config/config.xml  |  2 +-
 4 files changed, 64 insertions(+), 15 deletions(-)

diff --git a/composer.json b/composer.json
index b98631e..794c815 100644
--- a/composer.json
+++ b/composer.json
@@ -29,6 +29,7 @@
         "symfony/dependency-injection": "^3.4 | ^4.0",
         "symfony/http-kernel": "^3.4 | ^4.0",
         "symfony/property-info": "^3.4 | ^4.0",
+        "symfony/routing": "^3.4 | ^4.0",
         "symfony/serializer": "^3.4 | ^4.0",
         "twig/twig": "^2.2"
     },
diff --git a/src/php/Metadata/MetadataFactory.php b/src/php/Metadata/MetadataFactory.php
index e5650d1..e81918e 100644
--- a/src/php/Metadata/MetadataFactory.php
+++ b/src/php/Metadata/MetadataFactory.php
@@ -25,6 +25,8 @@ use ApiPlatform\Core\Api\FilterInterface;
 use ApiPlatform\Core\Api\OperationMethodResolverInterface;
 use ApiPlatform\Core\Api\OperationType;
 use ApiPlatform\Core\Api\ResourceClassResolverInterface;
+use ApiPlatform\Core\Exception\PropertyNotFoundException;
+use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
 use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
 use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
 use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
@@ -36,6 +38,8 @@ use Irstea\NgModelGeneratorBundle\Models\ClassName;
 use Irstea\NgModelGeneratorBundle\Models\PHPClass;
 use Psr\Container\ContainerInterface;
 use Symfony\Component\PropertyInfo\PropertyInfoExtractorInterface;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
+use Symfony\Component\Routing\RouterInterface;
 
 /**
  * Class MetadataFactory
@@ -79,6 +83,9 @@ final class MetadataFactory implements MetadataFactoryInterface
     /** @var string[][] */
     private $defaultGroups = [];
 
+    /** @var RouterInterface */
+    private $router;
+
     /**
      * MetadataFactory constructor.
      *
@@ -88,9 +95,9 @@ final class MetadataFactory implements MetadataFactoryInterface
      * @param PropertyMetadataFactoryInterface       $propertyMetadataFactory
      * @param PropertyInfoExtractorInterface         $propertyInfoExtractor
      * @param OperationMethodResolverInterface       $operationMethodResolver
-     * @param OperationPathResolverInterface         $operationPathResolver
      * @param ContainerInterface                     $filterLocator
      * @param PaginationMetadata                     $paginationMetadata
+     * @param RouterInterface                        $router
      * @param ClassHierarchy                         $classHierarchy
      */
     public function __construct(
@@ -100,7 +107,7 @@ final class MetadataFactory implements MetadataFactoryInterface
         PropertyMetadataFactoryInterface $propertyMetadataFactory,
         PropertyInfoExtractorInterface $propertyInfoExtractor,
         OperationMethodResolverInterface $operationMethodResolver,
-        OperationPathResolverInterface $operationPathResolver,
+        RouterInterface $router,
         ContainerInterface $filterLocator,
         PaginationMetadata $paginationMetadata,
         ClassHierarchy $classHierarchy
@@ -111,10 +118,10 @@ final class MetadataFactory implements MetadataFactoryInterface
         $this->propertyMetadataFactory = $propertyMetadataFactory;
         $this->propertyInfoExtractor = $propertyInfoExtractor;
         $this->operationMethodResolver = $operationMethodResolver;
-        $this->operationPathResolver = $operationPathResolver;
         $this->filterLocator = $filterLocator;
         $this->paginationMetadata = $paginationMetadata;
         $this->classHierarchy = $classHierarchy;
+        $this->router = $router;
     }
 
     /**
@@ -182,6 +189,10 @@ final class MetadataFactory implements MetadataFactoryInterface
     /**
      * @param ClassName $class
      *
+     * @throws PropertyNotFoundException
+     * @throws ResourceClassNotFoundException
+     * @throws \ReflectionException
+     *
      * @return OperationMetadata[]
      */
     private function getOperations(ClassName $class): array
@@ -212,6 +223,10 @@ final class MetadataFactory implements MetadataFactoryInterface
      * @param string              $type
      * @param array               $operation
      *
+     * @throws PropertyNotFoundException
+     * @throws ResourceClassNotFoundException
+     * @throws \ReflectionException
+     *
      * @return OperationMetadata
      */
     private function getOperation(
@@ -229,11 +244,8 @@ final class MetadataFactory implements MetadataFactoryInterface
 
         $shortName = $resourceMetadata->getShortName();
         Assertion::notNull($shortName);
-        /** @noinspection PhpMethodParametersCountMismatchInspection */
-        $path = $this->operationPathResolver->resolveOperationPath($shortName, $operation, $type);
 
-        // Suppression du suffixe de format, qui n'est pas utilisable en dehors de Symfony
-        $path = \str_replace('.{_format}', '', $path);
+        $path = $this->getOperationPath($class, $type, $name, $method);
 
         $getAttribute = function (string $attrName, $default) use ($resourceMetadata, $type, $name) {
             return $resourceMetadata->getTypedOperationAttribute($type, $name, $attrName, $default, true);
@@ -303,6 +315,38 @@ final class MetadataFactory implements MetadataFactoryInterface
         );
     }
 
+    /**
+     * @param ClassName $class
+     * @param string    $type
+     * @param string    $name
+     * @param string    $method
+     *
+     * @return string
+     */
+    private function getOperationPath(ClassName $class, string $type, string $name, string $method): string
+    {
+        $className = $class->getFullName();
+        $path = null;
+
+        foreach ($this->router->getRouteCollection() as $route) {
+            if (
+                 $route->getDefault('_api_resource_class') === $className
+                 && $route->getDefault("_api_${type}_operation_name") === $name
+                 && \in_array($method, $route->getMethods(), true)) {
+                $path = $route->getPath();
+                break;
+            }
+        }
+
+        if (!$path) {
+            throw new RouteNotFoundException("No route found for ${$type} operation ${name} on ${className}");
+        }
+
+        $path = str_replace('.{_format}', '', $path);
+
+        return $path;
+    }
+
     /**
      * @param ClassName $class
      * @param array     $filterIds
@@ -332,8 +376,8 @@ final class MetadataFactory implements MetadataFactoryInterface
      * @param bool              $normalization
      * @param string[]          $groups
      *
-     * @throws \ApiPlatform\Core\Exception\PropertyNotFoundException
-     * @throws \ApiPlatform\Core\Exception\ResourceClassNotFoundException
+     * @throws PropertyNotFoundException
+     * @throws ResourceClassNotFoundException
      * @throws \ReflectionException
      *
      * @return SerializationMetadata
@@ -360,8 +404,8 @@ final class MetadataFactory implements MetadataFactoryInterface
      * @param OperationDef $opDef
      * @param string[]     $groups
      *
-     * @throws \ApiPlatform\Core\Exception\PropertyNotFoundException
-     * @throws \ApiPlatform\Core\Exception\ResourceClassNotFoundException
+     * @throws PropertyNotFoundException
+     * @throws ResourceClassNotFoundException
      * @throws \ReflectionException
      *
      * @return SerializationMetadata
@@ -442,6 +486,8 @@ final class MetadataFactory implements MetadataFactoryInterface
      * @param OperationDef|null $opDef
      * @param array             $groups
      *
+     * @throws ResourceClassNotFoundException
+     *
      * @return string
      */
     private function getRepresentationName(
@@ -477,6 +523,8 @@ final class MetadataFactory implements MetadataFactoryInterface
     /**
      * @param ClassName $class
      *
+     * @throws ResourceClassNotFoundException
+     *
      * @return array
      */
     private function getDefaultGroups(ClassName $class): array
diff --git a/src/php/Models/ClassInfo.php b/src/php/Models/ClassInfo.php
index 066a146..9d57ed7 100644
--- a/src/php/Models/ClassInfo.php
+++ b/src/php/Models/ClassInfo.php
@@ -61,10 +61,10 @@ final class ClassInfo implements ClassName
     /**
      * ClassInfo constructor.
      *
-     * @param ClassName $class
+     * @param ClassName          $class
      * @param PropertyMetadata[] $properties
-     * @param bool $abstract
-     * @param bool $resource
+     * @param bool               $abstract
+     * @param bool               $resource
      */
     public function __construct(ClassName $class, array $properties = [], bool $abstract = false, bool $resource = false)
     {
diff --git a/src/php/Resources/config/config.xml b/src/php/Resources/config/config.xml
index e77d8c8..20f06f5 100644
--- a/src/php/Resources/config/config.xml
+++ b/src/php/Resources/config/config.xml
@@ -14,7 +14,7 @@
             <argument type="service" id="api_platform.metadata.property.metadata_factory"/>
             <argument type="service" id="property_info"/>
             <argument type="service" id="api_platform.operation_method_resolver"/>
-            <argument type="service" id="api_platform.operation_path_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"/>
-- 
GitLab