vendor/api-platform/core/src/Core/Metadata/Resource/Factory/AnnotationResourceMetadataFactory.php line 100

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\Resource\Factory;
  12. use ApiPlatform\Core\Annotation\ApiResource;
  13. use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
  14. use ApiPlatform\Exception\ResourceClassNotFoundException;
  15. use Doctrine\Common\Annotations\Reader;
  16. /**
  17. * Creates a resource metadata from {@see ApiResource} annotations.
  18. *
  19. * @author Kévin Dunglas <dunglas@gmail.com>
  20. */
  21. final class AnnotationResourceMetadataFactory implements ResourceMetadataFactoryInterface
  22. {
  23. private $reader;
  24. private $decorated;
  25. private $defaults;
  26. public function __construct(Reader $reader = null, ResourceMetadataFactoryInterface $decorated = null, array $defaults = [])
  27. {
  28. $this->reader = $reader;
  29. $this->decorated = $decorated;
  30. $this->defaults = $defaults + ['attributes' => []];
  31. }
  32. public function create(string $resourceClass): ResourceMetadata
  33. {
  34. $parentResourceMetadata = null;
  35. if ($this->decorated) {
  36. try {
  37. $parentResourceMetadata = $this->decorated->create($resourceClass);
  38. } catch (ResourceClassNotFoundException $resourceNotFoundException) {
  39. // Ignore not found exception from decorated factories
  40. }
  41. }
  42. try {
  43. $reflectionClass = new \ReflectionClass($resourceClass);
  44. } catch (\ReflectionException $reflectionException) {
  45. return $this->handleNotFound($parentResourceMetadata, $resourceClass);
  46. }
  47. if (\PHP_VERSION_ID >= 80000 && $attributes = $reflectionClass->getAttributes(ApiResource::class)) {
  48. return $this->createMetadata($attributes[0]->newInstance(), $parentResourceMetadata);
  49. }
  50. if (null === $this->reader) {
  51. $this->handleNotFound($parentResourceMetadata, $resourceClass);
  52. }
  53. $resourceAnnotation = $this->reader->getClassAnnotation($reflectionClass, ApiResource::class);
  54. if (!$resourceAnnotation instanceof ApiResource) {
  55. return $this->handleNotFound($parentResourceMetadata, $resourceClass);
  56. }
  57. return $this->createMetadata($resourceAnnotation, $parentResourceMetadata);
  58. }
  59. /**
  60. * Returns the metadata from the decorated factory if available or throws an exception.
  61. *
  62. * @throws ResourceClassNotFoundException
  63. */
  64. private function handleNotFound(?ResourceMetadata $parentPropertyMetadata, string $resourceClass): ResourceMetadata
  65. {
  66. if (null !== $parentPropertyMetadata) {
  67. return $parentPropertyMetadata;
  68. }
  69. throw new ResourceClassNotFoundException(sprintf('Resource "%s" not found.', $resourceClass));
  70. }
  71. private function createMetadata(ApiResource $annotation, ResourceMetadata $parentResourceMetadata = null): ResourceMetadata
  72. {
  73. $attributes = null;
  74. if (null !== $annotation->attributes || [] !== $this->defaults['attributes']) {
  75. $attributes = (array) $annotation->attributes;
  76. foreach ($this->defaults['attributes'] as $key => $value) {
  77. if (!isset($attributes[$key])) {
  78. $attributes[$key] = $value;
  79. }
  80. }
  81. }
  82. if (!$parentResourceMetadata) {
  83. return new ResourceMetadata(
  84. $annotation->shortName,
  85. $annotation->description ?? $this->defaults['description'] ?? null, // @phpstan-ignore-line
  86. $annotation->iri ?? $this->defaults['iri'] ?? null, // @phpstan-ignore-line
  87. $annotation->itemOperations ?? $this->defaults['item_operations'] ?? null, // @phpstan-ignore-line
  88. $annotation->collectionOperations ?? $this->defaults['collection_operations'] ?? null, // @phpstan-ignore-line
  89. $attributes,
  90. $annotation->subresourceOperations,
  91. $annotation->graphql ?? $this->defaults['graphql'] ?? null // @phpstan-ignore-line
  92. );
  93. }
  94. $resourceMetadata = $parentResourceMetadata;
  95. foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'subresourceOperations', 'graphql', 'attributes'] as $property) {
  96. $resourceMetadata = $this->createWith($resourceMetadata, $property, $annotation->{$property});
  97. }
  98. return $resourceMetadata;
  99. }
  100. /**
  101. * Creates a new instance of metadata if the property is not already set.
  102. */
  103. private function createWith(ResourceMetadata $resourceMetadata, string $property, $value): ResourceMetadata
  104. {
  105. $upperProperty = ucfirst($property);
  106. $getter = "get$upperProperty";
  107. if (null !== $resourceMetadata->{$getter}()) {
  108. return $resourceMetadata;
  109. }
  110. if (null === $value) {
  111. return $resourceMetadata;
  112. }
  113. $wither = "with$upperProperty";
  114. return $resourceMetadata->{$wither}($value);
  115. }
  116. }