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

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. /**
  33. * {@inheritdoc}
  34. */
  35. public function create(string $resourceClass): ResourceMetadata
  36. {
  37. $parentResourceMetadata = null;
  38. if ($this->decorated) {
  39. try {
  40. $parentResourceMetadata = $this->decorated->create($resourceClass);
  41. } catch (ResourceClassNotFoundException $resourceNotFoundException) {
  42. // Ignore not found exception from decorated factories
  43. }
  44. }
  45. try {
  46. $reflectionClass = new \ReflectionClass($resourceClass);
  47. } catch (\ReflectionException $reflectionException) {
  48. return $this->handleNotFound($parentResourceMetadata, $resourceClass);
  49. }
  50. if (\PHP_VERSION_ID >= 80000 && $attributes = $reflectionClass->getAttributes(ApiResource::class)) {
  51. return $this->createMetadata($attributes[0]->newInstance(), $parentResourceMetadata);
  52. }
  53. if (null === $this->reader) {
  54. $this->handleNotFound($parentResourceMetadata, $resourceClass);
  55. }
  56. $resourceAnnotation = $this->reader->getClassAnnotation($reflectionClass, ApiResource::class);
  57. if (!$resourceAnnotation instanceof ApiResource) {
  58. return $this->handleNotFound($parentResourceMetadata, $resourceClass);
  59. }
  60. return $this->createMetadata($resourceAnnotation, $parentResourceMetadata);
  61. }
  62. /**
  63. * Returns the metadata from the decorated factory if available or throws an exception.
  64. *
  65. * @throws ResourceClassNotFoundException
  66. */
  67. private function handleNotFound(?ResourceMetadata $parentPropertyMetadata, string $resourceClass): ResourceMetadata
  68. {
  69. if (null !== $parentPropertyMetadata) {
  70. return $parentPropertyMetadata;
  71. }
  72. throw new ResourceClassNotFoundException(sprintf('Resource "%s" not found.', $resourceClass));
  73. }
  74. private function createMetadata(ApiResource $annotation, ResourceMetadata $parentResourceMetadata = null): ResourceMetadata
  75. {
  76. $attributes = null;
  77. if (null !== $annotation->attributes || [] !== $this->defaults['attributes']) {
  78. $attributes = (array) $annotation->attributes;
  79. foreach ($this->defaults['attributes'] as $key => $value) {
  80. if (!isset($attributes[$key])) {
  81. $attributes[$key] = $value;
  82. }
  83. }
  84. }
  85. if (!$parentResourceMetadata) {
  86. return new ResourceMetadata(
  87. $annotation->shortName,
  88. $annotation->description ?? $this->defaults['description'] ?? null, // @phpstan-ignore-line
  89. $annotation->iri ?? $this->defaults['iri'] ?? null, // @phpstan-ignore-line
  90. $annotation->itemOperations ?? $this->defaults['item_operations'] ?? null, // @phpstan-ignore-line
  91. $annotation->collectionOperations ?? $this->defaults['collection_operations'] ?? null, // @phpstan-ignore-line
  92. $attributes,
  93. $annotation->subresourceOperations,
  94. $annotation->graphql ?? $this->defaults['graphql'] ?? null // @phpstan-ignore-line
  95. );
  96. }
  97. $resourceMetadata = $parentResourceMetadata;
  98. foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'subresourceOperations', 'graphql', 'attributes'] as $property) {
  99. $resourceMetadata = $this->createWith($resourceMetadata, $property, $annotation->{$property});
  100. }
  101. return $resourceMetadata;
  102. }
  103. /**
  104. * Creates a new instance of metadata if the property is not already set.
  105. *
  106. * @param mixed $value
  107. */
  108. private function createWith(ResourceMetadata $resourceMetadata, string $property, $value): ResourceMetadata
  109. {
  110. $upperProperty = ucfirst($property);
  111. $getter = "get$upperProperty";
  112. if (null !== $resourceMetadata->{$getter}()) {
  113. return $resourceMetadata;
  114. }
  115. if (null === $value) {
  116. return $resourceMetadata;
  117. }
  118. $wither = "with$upperProperty";
  119. return $resourceMetadata->{$wither}($value);
  120. }
  121. }