NelmioApiDocBundle/ModelDescriber/JMSModelDescriber.php

166 lines
5.8 KiB
PHP
Raw Normal View History

2017-06-25 15:40:07 +02:00
<?php
/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Nelmio\ApiDocBundle\ModelDescriber;
use EXSyst\Component\Swagger\Schema;
use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
use JMS\Serializer\SerializationContext;
use Metadata\MetadataFactoryInterface;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareInterface;
use Nelmio\ApiDocBundle\Describer\ModelRegistryAwareTrait;
use Nelmio\ApiDocBundle\Model\Model;
use Symfony\Component\PropertyInfo\Type;
/**
* Uses the JMS metadata factory to extract input/output model information.
*/
class JMSModelDescriber implements ModelDescriberInterface, ModelRegistryAwareInterface
{
use ModelRegistryAwareTrait;
private $factory;
2017-06-25 15:40:07 +02:00
private $namingStrategy;
private $swaggerPropertyAnnotationReader;
private $swaggerDefinitionAnnotationReader;
2017-12-19 08:41:24 +01:00
private $phpdocPropertyAnnotationsReader;
public function __construct(
MetadataFactoryInterface $factory,
PropertyNamingStrategyInterface $namingStrategy,
2017-12-19 08:41:24 +01:00
SwaggerPropertyAnnotationReader $swaggerPropertyAnnotationReader,
SwaggerDefinitionAnnotationReader $swaggerDefinitionAnnotationReader,
PhpdocPropertyAnnotationReader $phpdocPropertyAnnotationReader = null
2017-12-17 10:44:07 +01:00
) {
2017-06-25 15:40:07 +02:00
$this->factory = $factory;
$this->namingStrategy = $namingStrategy;
$this->swaggerPropertyAnnotationReader = $swaggerPropertyAnnotationReader;
$this->swaggerDefinitionAnnotationReader = $swaggerDefinitionAnnotationReader;
2017-12-19 08:41:24 +01:00
$this->phpdocPropertyAnnotationsReader = $phpdocPropertyAnnotationReader;
2017-06-25 15:40:07 +02:00
}
/**
* {@inheritdoc}
*/
public function describe(Model $model, Schema $schema)
{
$className = $model->getType()->getClassName();
$metadata = $this->factory->getMetadataForClass($className);
if (null === $metadata) {
throw new \InvalidArgumentException(sprintf('No metadata found for class %s.', $className));
}
$groupsExclusion = null !== $model->getGroups() ? new GroupsExclusionStrategy($model->getGroups()) : null;
$schema->setType('object');
$this->swaggerDefinitionAnnotationReader->updateWithSwaggerDefinitionAnnotation(new \ReflectionClass($className), $schema);
2017-06-25 15:40:07 +02:00
$properties = $schema->getProperties();
foreach ($metadata->propertyMetadata as $item) {
// filter groups
if (null !== $groupsExclusion && $groupsExclusion->shouldSkipProperty($item, SerializationContext::create())) {
continue;
}
$name = $this->namingStrategy->translateName($item);
$property = $properties->get($name);
// read property options from Swagger Property annotation if it exists
if (null !== $item->reflection) {
if ($this->phpdocPropertyAnnotationsReader) {
$this->phpdocPropertyAnnotationsReader->updateWithPhpdoc($item->reflection, $property);
}
$this->swaggerPropertyAnnotationReader->updateWithSwaggerPropertyAnnotation($item->reflection, $property);
}
if (null !== $property->getType()) {
continue;
}
if (null === $item->type) {
$properties->remove($name);
}
2017-06-25 15:40:07 +02:00
if ($type = $this->getNestedTypeInArray($item)) {
$property->setType('array');
$property = $property->getItems();
} else {
$type = $item->type['name'];
}
if (in_array($type, ['boolean', 'string', 'array'])) {
2017-06-25 15:40:07 +02:00
$property->setType($type);
} elseif (in_array($type, ['int', 'integer'])) {
$property->setType('integer');
} elseif (in_array($type, ['double', 'float'])) {
$property->setType('number');
$property->setFormat($type);
} elseif (in_array($type, ['DateTime', 'DateTimeImmutable'])) {
2017-06-25 15:40:07 +02:00
$property->setType('string');
$property->setFormat('date-time');
} else {
// we can use property type also for custom handlers, then we don't have here real class name
if (!class_exists($type)) {
continue;
}
2018-01-04 11:34:23 +01:00
if (!isset($model->getGroups()[$name]) || !is_array($model->getGroups()[$name])) {
2017-12-07 14:00:40 +08:00
$groups = $model->getGroups();
2017-12-18 08:06:19 +08:00
} else {
2017-12-07 14:00:40 +08:00
$groups = $model->getGroups()[$name];
2017-12-18 08:06:19 +08:00
}
2017-12-07 14:00:40 +08:00
2017-06-25 15:40:07 +02:00
$property->setRef(
2017-12-07 14:00:40 +08:00
$this->modelRegistry->register(new Model(new Type(Type::BUILTIN_TYPE_OBJECT, false, $type), $groups))
2017-06-25 15:40:07 +02:00
);
}
}
}
/**
* {@inheritdoc}
*/
public function supports(Model $model): bool
{
$className = $model->getType()->getClassName();
2017-12-22 17:42:18 +00:00
2017-06-25 15:40:07 +02:00
try {
if ($this->factory->getMetadataForClass($className)) {
return true;
}
} catch (\ReflectionException $e) {
}
return false;
}
private function getNestedTypeInArray(PropertyMetadata $item)
{
if ('array' !== $item->type['name'] && 'ArrayCollection' !== $item->type['name']) {
return;
}
// array<string, MyNamespaceMyObject>
if (isset($item->type['params'][1]['name'])) {
return $item->type['params'][1]['name'];
}
// array<MyNamespaceMyObject>
if (isset($item->type['params'][0]['name'])) {
return $item->type['params'][0]['name'];
}
}
}