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

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