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

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