An error occurred while loading the file. Please try again.
-
Guillaume Perréal authoreddceb9d80
<?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();
}
}
}