PropertyMetadataFactory.php 7.85 KiB
<?php declare(strict_types=1);
/*
 * irstea/ng-model-generator-bundle generates Typescript interfaces for Angular using api-platform metadata.
 * Copyright (C) 2018 IRSTEA
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License and the GNU
 * Lesser General Public License along with this program. If not, see
 * <https://www.gnu.org/licenses/>.
namespace Irstea\NgModelGeneratorBundle\Metadata;
use ApiPlatform\Core\Api\ResourceClassResolverInterface;
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
use ApiPlatform\Core\Metadata\Property\PropertyMetadata as APIPropertyMetadata;
use Irstea\NgModelGeneratorBundle\Models\ClassName;
use Irstea\NgModelGeneratorBundle\Models\PHPClass;
use Symfony\Component\PropertyInfo\Type;
/**
 * Class PropertyMetadataFactory.
class PropertyMetadataFactory
    public const MODE_CREATE = 'CREATE';
    public const MODE_UPDATE = 'UPDATE';
    public const MODE_READ = 'READ';
    public const MODE_OTHER = 'OTHER';
    /** @var PropertyNameCollectionFactoryInterface */
    private $propertyNameCollectionFactory;
    /** @var PropertyMetadataFactoryInterface */
    private $propertyMetadataFactory;
    /** @var ClassName */
    private $resource;
    /** @var string */
    private $mode;
    /** @var array */
    private $groups;
    /** @var array */
    private $properties = [];
    /** @var ResourceClassResolverInterface */
    private $resourceClassResolver;
    /**
     * PropertyMetadataFactory constructor.
     * @param ResourceClassResolverInterface         $resourceClassResolver
     * @param PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory
     * @param PropertyMetadataFactoryInterface       $propertyMetadataFactory
     * @param ClassName                              $resource
     * @param string                                 $mode
     * @param array                                  $groups
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
*/ public function __construct( ResourceClassResolverInterface $resourceClassResolver, PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, ClassName $resource, string $mode, array $groups ) { $this->propertyNameCollectionFactory = $propertyNameCollectionFactory; $this->propertyMetadataFactory = $propertyMetadataFactory; $this->resource = $resource; $this->mode = $mode; $this->groups = $groups; $this->resourceClassResolver = $resourceClassResolver; } /** * @param ClassName $class * @param string $propertyName * * @throws ResourceClassNotFoundException * @throws \ApiPlatform\Core\Exception\PropertyNotFoundException * * @return PropertyMetadata */ public function create(ClassName $class, string $propertyName): PropertyMetadata { $propertyMeta = $this->getAPIMetadata($class)[$propertyName]; $typeMeta = $propertyMeta->getType(); \assert($typeMeta !== null); [$link, $embedded] = $this->getLinkStatus($propertyMeta); $nullable = $this->mode === self::MODE_UPDATE || $typeMeta->isNullable(); return new PropertyMetadata( $propertyName, '', $typeMeta, $propertyMeta->isIdentifier(), $nullable, $propertyMeta->isReadable(), $propertyMeta->isWritable(), (bool) $propertyMeta->isInitializable(), $link, $embedded ); } /** * @param $propertyMeta * * @return array */ private function getLinkStatus(APIPropertyMetadata $propertyMeta): array { $typeMeta = $propertyMeta->getType(); \assert($typeMeta !== null); $leafType = $this->getLeafType($typeMeta); if (!$leafType) { return [false, false]; } $leafClassName = $leafType->getClassName(); if (!$leafClassName || !$this->resourceClassResolver->isResourceClass($leafClassName)) { return [false, false]; }
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
$leafClass = PHPClass::get($leafClassName); $embedded = $this->mode === self::MODE_READ ? $propertyMeta->isReadableLink() : $propertyMeta->isWritableLink(); if (!\is_bool($embedded)) { $leafProperties = $this->getAPIMetadata($leafClass); foreach ($leafProperties as $leafProperty) { if (!$leafProperty->isIdentifier()) { $embedded = true; break; } } } return [true, (bool) $embedded]; } /** * @param Type $type * * @return Type|null */ private function getLeafType(Type $type): ?Type { while ($type && $type->isCollection()) { $type = $type->getCollectionValueType(); } return $type; } /** * @param ClassName $class * * @throws ResourceClassNotFoundException * @throws \ApiPlatform\Core\Exception\PropertyNotFoundException * * @return APIPropertyMetadata[] */ public function getAPIMetadata(ClassName $class): array { $key = $class->getFullName(); if (!isset($this->properties[$key])) { $this->properties[$key] = $this->doGetAPIMetadata($class); } return $this->properties[$key]; } /** * @param ClassName $class * * @throws \ApiPlatform\Core\Exception\PropertyNotFoundException * @throws ResourceClassNotFoundException * * @return APIPropertyMetadata[] */ private function doGetAPIMetadata(ClassName $class): array { $properties = []; $options = $this->groups ? ['serializer_groups' => $this->groups] : []; $isResource = $class->getFullName() === $this->resource->getFullName(); foreach ($this->propertyNameCollectionFactory->create($class->getFullName(), $options) as $propertyName) { \assert(\is_string($propertyName)); $propertyMeta = $this->propertyMetadataFactory->create($class->getFullName(), $propertyName); if (!$propertyMeta->getType() || $propertyMeta->isChildInherited()) { continue;
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
} if (!$this->acceptProperty($isResource, $propertyMeta)) { continue; } $properties[$propertyName] = $propertyMeta; } return $properties; } /** * @param string $mode * @param APIPropertyMetadata $propertyMeta * @param mixed $isResource * * @return bool */ private function acceptProperty(bool $isResource, APIPropertyMetadata $propertyMeta): bool { if (!$isResource && $propertyMeta->isIdentifier()) { return true; } switch ($this->mode) { case self::MODE_CREATE: return $propertyMeta->isWritable() || $propertyMeta->isInitializable(); case self::MODE_READ: return $propertyMeta->isReadable(); case self::MODE_UPDATE: return $propertyMeta->isWritable(); default: return $propertyMeta->isReadable() || $propertyMeta->isWritable(); } } }