vendor/api-platform/core/src/Core/Metadata/Property/Factory/AnnotationSubresourceMetadataFactory.php line 65

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the API Platform project.
  4.  *
  5.  * (c) Kévin Dunglas <dunglas@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Core\Metadata\Property\Factory;
  12. use ApiPlatform\Core\Annotation\ApiSubresource;
  13. use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
  14. use ApiPlatform\Core\Metadata\Property\SubresourceMetadata;
  15. use ApiPlatform\Exception\InvalidResourceException;
  16. use ApiPlatform\Util\Reflection;
  17. use Doctrine\Common\Annotations\Reader;
  18. use Symfony\Component\PropertyInfo\Type;
  19. /**
  20.  * Adds subresources to the properties metadata from {@see ApiResource} annotations.
  21.  *
  22.  * @author Antoine Bluchet <soyuka@gmail.com>
  23.  */
  24. final class AnnotationSubresourceMetadataFactory implements PropertyMetadataFactoryInterface
  25. {
  26.     private $reader;
  27.     private $decorated;
  28.     public function __construct(Reader $reader nullPropertyMetadataFactoryInterface $decorated)
  29.     {
  30.         $this->reader $reader;
  31.         $this->decorated $decorated;
  32.     }
  33.     /**
  34.      * {@inheritdoc}
  35.      */
  36.     public function create(string $resourceClassstring $property, array $options = []): PropertyMetadata
  37.     {
  38.         $propertyMetadata $this->decorated->create($resourceClass$property$options);
  39.         try {
  40.             $reflectionClass = new \ReflectionClass($resourceClass);
  41.         } catch (\ReflectionException $reflectionException) {
  42.             return $propertyMetadata;
  43.         }
  44.         if ($reflectionClass->hasProperty($property)) {
  45.             $reflectionProperty $reflectionClass->getProperty($property);
  46.             if (\PHP_VERSION_ID >= 80000 && $attributes $reflectionProperty->getAttributes(ApiSubresource::class)) {
  47.                 return $this->updateMetadata($attributes[0]->newInstance(), $propertyMetadata$resourceClass$property);
  48.             }
  49.             if (!$this->reader) {
  50.                 return $propertyMetadata;
  51.             }
  52.             $annotation $this->reader->getPropertyAnnotation($reflectionPropertyApiSubresource::class);
  53.             if ($annotation instanceof ApiSubresource) {
  54.                 trigger_deprecation('api-platform/core''2.7'sprintf('Declare a new resource instead of using ApiSubresource on the property "%s".'$property));
  55.                 return $this->updateMetadata($annotation$propertyMetadata$resourceClass$property);
  56.             }
  57.         }
  58.         foreach (array_merge(Reflection::ACCESSOR_PREFIXESReflection::MUTATOR_PREFIXES) as $prefix) {
  59.             $methodName $prefix.ucfirst($property);
  60.             if (!$reflectionClass->hasMethod($methodName)) {
  61.                 continue;
  62.             }
  63.             $reflectionMethod $reflectionClass->getMethod($methodName);
  64.             if (!$reflectionMethod->isPublic()) {
  65.                 continue;
  66.             }
  67.             if (\PHP_VERSION_ID >= 80000 && $attributes $reflectionMethod->getAttributes(ApiSubresource::class)) {
  68.                 return $this->updateMetadata($attributes[0]->newInstance(), $propertyMetadata$resourceClass$property);
  69.             }
  70.             if (!$this->reader) {
  71.                 return $propertyMetadata;
  72.             }
  73.             $annotation $this->reader->getMethodAnnotation($reflectionMethodApiSubresource::class);
  74.             if ($annotation instanceof ApiSubresource) {
  75.                 return $this->updateMetadata($annotation$propertyMetadata$resourceClass$property);
  76.             }
  77.         }
  78.         return $propertyMetadata;
  79.     }
  80.     private function updateMetadata(ApiSubresource $annotationPropertyMetadata $propertyMetadatastring $originResourceClassstring $propertyName): PropertyMetadata
  81.     {
  82.         // TODO: 3.0 support multiple types, default value of types will be [] instead of null
  83.         $type $propertyMetadata->getType();
  84.         if (null === $type) {
  85.             throw new InvalidResourceException(sprintf('Property "%s" on resource "%s" is declared as a subresource, but its type could not be determined.'$propertyName$originResourceClass));
  86.         }
  87.         $isCollection $type->isCollection();
  88.         if (
  89.             $isCollection &&
  90.             $collectionValueType method_exists(Type::class, 'getCollectionValueTypes') ? ($type->getCollectionValueTypes()[0] ?? null) : $type->getCollectionValueType()
  91.         ) {
  92.             $resourceClass $collectionValueType->getClassName();
  93.         } else {
  94.             $resourceClass $type->getClassName();
  95.         }
  96.         $maxDepth $annotation->maxDepth;
  97.         // @ApiSubresource is on the class identifier (/collection/{id}/subcollection/{subcollectionId})
  98.         if (null === $resourceClass) {
  99.             $resourceClass $originResourceClass;
  100.             $isCollection false;
  101.         }
  102.         return $propertyMetadata->withSubresource(new SubresourceMetadata($resourceClass$isCollection$maxDepth));
  103.     }
  104. }