vendor/api-platform/core/src/Core/Metadata/Property/Factory/ExtractorPropertyMetadataFactory.php line 42

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\Exception\PropertyNotFoundException;
  13. use ApiPlatform\Core\Metadata\Extractor\ExtractorInterface;
  14. use ApiPlatform\Core\Metadata\Property\PropertyMetadata;
  15. use ApiPlatform\Core\Metadata\Property\SubresourceMetadata;
  16. use ApiPlatform\Metadata\Extractor\ResourceExtractorInterface;
  17. use Symfony\Component\PropertyInfo\Type;
  18. /**
  19.  * Creates properties's metadata using an extractor.
  20.  *
  21.  * @author Kévin Dunglas <dunglas@gmail.com>
  22.  */
  23. final class ExtractorPropertyMetadataFactory implements PropertyMetadataFactoryInterface
  24. {
  25.     private $extractor;
  26.     private $decorated;
  27.     /**
  28.      * @param ResourceExtractorInterface|ExtractorInterface $extractor
  29.      */
  30.     public function __construct($extractorPropertyMetadataFactoryInterface $decorated null)
  31.     {
  32.         $this->extractor $extractor;
  33.         $this->decorated $decorated;
  34.     }
  35.     public function create(string $resourceClassstring $property, array $options = []): PropertyMetadata
  36.     {
  37.         $parentPropertyMetadata null;
  38.         if ($this->decorated) {
  39.             try {
  40.                 $parentPropertyMetadata $this->decorated->create($resourceClass$property$options);
  41.             } catch (PropertyNotFoundException $propertyNotFoundException) {
  42.                 // Ignore not found exception from decorated factories
  43.             }
  44.         }
  45.         $isInterface interface_exists($resourceClass);
  46.         if (
  47.             !property_exists($resourceClass$property) && !$isInterface
  48.             || null === ($propertyMetadata $this->extractor->getResources()[$resourceClass]['properties'][$property] ?? null)
  49.         ) {
  50.             return $this->handleNotFound($parentPropertyMetadata$resourceClass$property);
  51.         }
  52.         if ($parentPropertyMetadata) {
  53.             return $this->update($parentPropertyMetadata$propertyMetadata);
  54.         }
  55.         return ($metadata = new PropertyMetadata(
  56.             null,
  57.             $propertyMetadata['description'],
  58.             $propertyMetadata['readable'],
  59.             $propertyMetadata['writable'],
  60.             $propertyMetadata['readableLink'],
  61.             $propertyMetadata['writableLink'],
  62.             $propertyMetadata['required'],
  63.             $propertyMetadata['identifier'],
  64.             $propertyMetadata['iri'],
  65.             null,
  66.             $propertyMetadata['attributes']
  67.         ))->withSubresource($this->createSubresourceMetadata($propertyMetadata['subresource'], $metadata));
  68.     }
  69.     /**
  70.      * Returns the metadata from the decorated factory if available or throws an exception.
  71.      *
  72.      * @throws PropertyNotFoundException
  73.      */
  74.     private function handleNotFound(?PropertyMetadata $parentPropertyMetadatastring $resourceClassstring $property): PropertyMetadata
  75.     {
  76.         if ($parentPropertyMetadata) {
  77.             return $parentPropertyMetadata;
  78.         }
  79.         throw new PropertyNotFoundException(sprintf('Property "%s" of the resource class "%s" not found.'$property$resourceClass));
  80.     }
  81.     /**
  82.      * Creates a new instance of metadata if the property is not already set.
  83.      */
  84.     private function update(PropertyMetadata $propertyMetadata, array $metadata): PropertyMetadata
  85.     {
  86.         $metadataAccessors = [
  87.             'description' => 'get',
  88.             'readable' => 'is',
  89.             'writable' => 'is',
  90.             'writableLink' => 'is',
  91.             'readableLink' => 'is',
  92.             'required' => 'is',
  93.             'identifier' => 'is',
  94.             'iri' => 'get',
  95.             'attributes' => 'get',
  96.         ];
  97.         foreach ($metadataAccessors as $metadataKey => $accessorPrefix) {
  98.             if (null === $metadata[$metadataKey]) {
  99.                 continue;
  100.             }
  101.             $propertyMetadata $propertyMetadata->{'with'.ucfirst($metadataKey)}($metadata[$metadataKey]);
  102.         }
  103.         if ($propertyMetadata->hasSubresource()) {
  104.             return $propertyMetadata;
  105.         }
  106.         return $propertyMetadata->withSubresource($this->createSubresourceMetadata($metadata['subresource'], $propertyMetadata));
  107.     }
  108.     /**
  109.      * Creates a SubresourceMetadata.
  110.      *
  111.      * @param bool|array|null  $subresource      the subresource metadata coming from XML or YAML
  112.      * @param PropertyMetadata $propertyMetadata the current property metadata
  113.      */
  114.     private function createSubresourceMetadata($subresourcePropertyMetadata $propertyMetadata): ?SubresourceMetadata
  115.     {
  116.         if (!$subresource) {
  117.             return null;
  118.         }
  119.         $type $propertyMetadata->getType();
  120.         $maxDepth = \is_array($subresource) ? $subresource['maxDepth'] ?? null null;
  121.         if (null !== $type) {
  122.             $isCollection $type->isCollection();
  123.             if (
  124.                 $isCollection
  125.                 && $collectionValueType method_exists(Type::class, 'getCollectionValueTypes') ? ($type->getCollectionValueTypes()[0] ?? null) : $type->getCollectionValueType()
  126.             ) {
  127.                 $resourceClass $collectionValueType->getClassName();
  128.             } else {
  129.                 $resourceClass $type->getClassName();
  130.             }
  131.         } elseif (\is_array($subresource) && isset($subresource['resourceClass'])) {
  132.             $resourceClass $subresource['resourceClass'];
  133.             $isCollection $subresource['collection'] ?? true;
  134.         } else {
  135.             return null;
  136.         }
  137.         return new SubresourceMetadata($resourceClass$isCollection$maxDepth);
  138.     }
  139. }