Last active
July 24, 2021 14:41
-
-
Save stloyd/e3f69a40b66ccae5bbaea231aeb51de3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\ApiPlatform\Metadata\Resource\Factory; | |
use ApiPlatform\Core\Annotation\ApiResource; | |
use ApiPlatform\Core\Exception\ResourceClassNotFoundException; | |
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; | |
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata; | |
use Doctrine\Common\Annotations\Reader; | |
final class AnnotationResourceMetadataFactory implements ResourceMetadataFactoryInterface | |
{ | |
private ?Reader $reader; | |
private ?ResourceMetadataFactoryInterface $decorated; | |
private $defaults; | |
public function __construct(Reader $reader, ?ResourceMetadataFactoryInterface $decorated = null, array $defaults = []) | |
{ | |
$this->reader = $reader; | |
$this->decorated = $decorated; | |
$this->defaults = $defaults + ['attributes' => []]; | |
} | |
/** | |
* {@inheritdoc} | |
*/ | |
public function create(string $resourceClass): ResourceMetadata | |
{ | |
$parentResourceMetadata = null; | |
if ($this->decorated) { | |
try { | |
$parentResourceMetadata = $this->decorated->create($resourceClass); | |
} catch (ResourceClassNotFoundException $resourceNotFoundException) { | |
// Ignore not found exception from decorated factories | |
} | |
} | |
try { | |
$reflectionClass = new \ReflectionClass($resourceClass); | |
} catch (\ReflectionException $reflectionException) { | |
return $this->handleNotFound($parentResourceMetadata, $resourceClass); | |
} | |
if (\PHP_VERSION_ID >= 80000 && $attributes = $reflectionClass->getAttributes(ApiResource::class)) { | |
return $this->createMetadata($attributes[0]->newInstance(), $parentResourceMetadata); | |
} | |
if (null === $this->reader) { | |
$this->handleNotFound($parentResourceMetadata, $resourceClass); | |
} | |
$resourceAnnotation = $this->reader->getClassAnnotation($reflectionClass, ApiResource::class); | |
if (!$resourceAnnotation instanceof ApiResource) { | |
return $this->handleNotFound($parentResourceMetadata, $resourceClass); | |
} | |
return $this->createMetadata($resourceAnnotation, $parentResourceMetadata); | |
} | |
/** | |
* Returns the metadata from the decorated factory if available or throws an exception. | |
* | |
* @throws ResourceClassNotFoundException | |
*/ | |
private function handleNotFound(?ResourceMetadata $parentPropertyMetadata, string $resourceClass): ResourceMetadata | |
{ | |
if (null !== $parentPropertyMetadata) { | |
return $parentPropertyMetadata; | |
} | |
throw new ResourceClassNotFoundException(sprintf('Resource "%s" not found.', $resourceClass)); | |
} | |
private function createMetadata(ApiResource $annotation, ?ResourceMetadata $parentResourceMetadata = null): ResourceMetadata | |
{ | |
$attributes = null; | |
if (null !== $annotation->attributes || [] !== $this->defaults['attributes']) { | |
$attributes = (array) $annotation->attributes; | |
foreach ($this->defaults['attributes'] as $key => $value) { | |
if (!isset($attributes[$key])) { | |
$attributes[$key] = $value; | |
} | |
} | |
} | |
if (!$parentResourceMetadata) { | |
return new ResourceMetadata( | |
$annotation->shortName, | |
$annotation->description ?? $this->defaults['description'] ?? null, | |
$annotation->iri ?? $this->defaults['iri'] ?? null, | |
$annotation->itemOperations ?? $this->defaults['item_operations'] ?? null, | |
$annotation->collectionOperations ?? $this->defaults['collection_operations'] ?? null, | |
$attributes, | |
$annotation->subresourceOperations, | |
$annotation->graphql ?? $this->defaults['graphql'] ?? null | |
); | |
} | |
$resourceMetadata = $parentResourceMetadata; | |
foreach (['shortName', 'description', 'iri', 'itemOperations', 'collectionOperations', 'subresourceOperations', 'graphql', 'attributes'] as $property) { | |
$resourceMetadata = $this->createWith($resourceMetadata, $property, $annotation->{$property}); | |
} | |
return $resourceMetadata; | |
} | |
/** | |
* Creates a new instance of metadata if the property is not already set. | |
*/ | |
private function createWith(ResourceMetadata $resourceMetadata, string $property, $value): ResourceMetadata | |
{ | |
$upperProperty = ucfirst($property); | |
$getter = "get{$upperProperty}"; | |
$currentValue = $resourceMetadata->{$getter}(); | |
if (null !== $currentValue) { | |
if (null === $value) { | |
$value = $currentValue; | |
} elseif (is_array($currentValue)) { | |
$value = array_merge($currentValue, $value); | |
} | |
} | |
if (null === $value) { | |
return $resourceMetadata; | |
} | |
$wither = "with{$upperProperty}"; | |
return $resourceMetadata->{$wither}($value); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# config/services.yaml | |
services: | |
# your current config here | |
# ... | |
'api_platform.metadata.resource.metadata_factory.annotation': | |
autowire: false | |
class: App\ApiPlatform\Metadata\Resource\Factory | |
decorates: 'api_platform.metadata.resource.metadata_factory' | |
decoration_priority: 40 | |
arguments: | |
- '@annotation_reader' | |
- '@api_platform.metadata.resource.metadata_factory.annotation.inner' | |
- '%api_platform.defaults%' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment